├── .gitignore
├── Dia2Lib
└── Dia2Lib.props
├── GenDia2Lib.bat
├── LICENSE
├── README.md
├── TypeTreeDumper.Bootstrapper
├── Program.cs
└── TypeTreeDumper.Bootstrapper.csproj
├── TypeTreeDumper.Loader
├── FallbackLoader.cs
└── TypeTreeDumper.Loader.csproj
├── TypeTreeDumper.sln
├── TypeTreeDumper
├── AsmSymbolResolver.cs
├── ConsoleLogger.cs
├── DiaSourceFactory.cs
├── DiaSymbolResolver.cs
├── Dumper.cs
├── DumperEngine.cs
├── EntryPoint.cs
├── EntryPointArgs.cs
├── Examples
│ └── ExamplePostProcessingPlugin.cs
├── ExportOptions.cs
├── IDumperEngine.cs
├── IDumperPlugin.cs
├── Logger.cs
├── MissingModuleException.cs
├── NameSearchOptions.cs
├── PluginManager.cs
├── RegisterDumperPluginAttribute.cs
├── TypeTreeDumper.csproj
├── TypeTreeUtility.cs
├── UnityClass.cs
├── UnityInfo.cs
├── UnityNode.cs
└── UnityString.cs
└── UnityInterop
├── BasicString.cs
├── CommonString.cs
├── DynamicArray.cs
├── HideFlags.cs
├── JsonHandler.cs
├── MemLabelId.cs
├── NameMangling.cs
├── NativeObject
├── NativeObject.V1.cs
├── NativeObject.V5_0.cs
├── NativeObject.cs
└── NativeObjectFactory.cs
├── ObjectCreationMode.cs
├── PersistentTypeID.cs
├── RuntimePlatform.cs
├── RuntimeType
├── RuntimeTypeArray.cs
├── RuntimeTypeInfo.V2017_3.cs
├── RuntimeTypeInfo.V3_4.cs
├── RuntimeTypeInfo.V5_0.cs
├── RuntimeTypeInfo.V5_2.cs
├── RuntimeTypeInfo.V5_4.cs
├── RuntimeTypeInfo.V5_5.cs
└── RuntimeTypeInfo.cs
├── SymbolResolver.cs
├── TransferInstructionFlags.cs
├── TransferMetaFlags.cs
├── TypeFlags.cs
├── TypeTree
├── TypeTree.V2019_1.cs
├── TypeTree.V2019_3.cs
├── TypeTree.V2022_2.cs
├── TypeTree.V2023_1.cs
├── TypeTree.V3_4.cs
├── TypeTree.V3_5.cs
├── TypeTree.V4_0.cs
├── TypeTree.V5_0.cs
├── TypeTree.V5_3.cs
├── TypeTree.cs
├── TypeTreeFactory.cs
├── TypeTreeNode.V1.cs
├── TypeTreeNode.V2019_1.cs
├── TypeTreeNode.V5_0.cs
└── TypeTreeNode.cs
├── UnityEngine.cs
├── UnityInterop.csproj
├── UnityVersion.cs
└── UnresolvedSymbolException.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | Dia2Lib/*.c
5 | Dia2Lib/*.h
6 | Dia2Lib/*.dll
7 | Dia2Lib/*.tlb
8 | packages
9 | launchsettings.json
--------------------------------------------------------------------------------
/Dia2Lib/Dia2Lib.props:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | $(VSInstallDir)
8 |
9 |
10 |
11 | $(VSInstallRoot)\DIA SDK
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/GenDia2Lib.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | title Dia2Lib Generator
3 |
4 | if not defined VSINSTALLDIR (
5 | echo This script must be called from a Visual Studio developer command prompt.
6 | echo Press any key to exit...
7 | pause >nul
8 | exit
9 | )
10 |
11 | if not exist "%VSINSTALLDIR%\DIA SDK" (
12 | echo The DIA SDK could not be found. Please install the C++ native tools for Visual Studio.
13 | echo Press any key to exit...
14 | pause >nul
15 | exit
16 | )
17 |
18 | echo Generating type library...
19 | set DIA=%VSINSTALLDIR%\DIA SDK
20 | midl /I "%DIA%\idl;%DIA%\include" dia2.idl /tlb dia2.tlb /out Dia2Lib
21 |
22 | echo Generating Dia2Lib.dll...
23 | tlbimp Dia2Lib\dia2.tlb /out:Dia2Lib\Dia2Lib.dll
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Benjamin Moir
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TypeTreeDumper
2 |
3 | This is an automated tool for dumping struct information from a Unity Installation.
4 |
5 | ## Building
6 |
7 | You will need to run `./GenDia2Lib.bat` in the Visual Studio powershell terminal in order to generate Dia2Lib.
8 |
9 | ## Disclaimer
10 |
11 | This software is not sponsored by or affiliated with Unity Technologies or its affiliates. "Unity" is a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere.
12 |
--------------------------------------------------------------------------------
/TypeTreeDumper.Bootstrapper/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Management;
6 | using System.Diagnostics;
7 | using System.Collections.Generic;
8 | using DetourSharp.Hosting;
9 | using CommandLine;
10 | using TerraFX.Interop.Windows;
11 | using static TerraFX.Interop.Windows.Windows;
12 | using static TerraFX.Interop.Windows.CREATE;
13 | using System.Runtime.InteropServices;
14 |
15 | namespace TypeTreeDumper
16 | {
17 | class Options
18 | {
19 | [Value(0, Required = true, HelpText = "Path to the Unity executable.")]
20 | public string UnityExePath { get; set; }
21 |
22 | [Option('o', "output", HelpText = "Directory to export to.")]
23 | public string OutputDirectory { get; set; }
24 |
25 | [Option("verbose", Default = false, HelpText = "Verbose logging output.")]
26 | public bool Verbose { get; set; }
27 |
28 | [Option('s', "silent", Default = false, HelpText = "No logging output except errors.")]
29 | public bool Silent { get; set; }
30 |
31 | [Option("debug", Default = false, HelpText = "If true, a debugger will be attached after entry into the Unity process.")]
32 | public bool Debug { get; set; }
33 | }
34 |
35 | class Program
36 | {
37 | const string DefaultRuntimeConfig = @"{
38 | ""runtimeOptions"": {
39 | ""tfm"": ""net6.0"",
40 | ""rollForward"": ""LatestMinor"",
41 | ""framework"": {
42 | ""name"": ""Microsoft.NETCore.App"",
43 | ""version"": ""6.0.0""
44 | },
45 | ""configProperties"": {
46 | ""System.Reflection.Metadata.MetadataUpdater.IsSupported"": false
47 | }
48 | }
49 | }";
50 |
51 | static void Main(string[] args)
52 | {
53 | Parser.Default.ParseArguments(args)
54 | .WithParsed(options => Run(options) );
55 | }
56 |
57 | static unsafe void Run(Options options)
58 | {
59 | var version = FileVersionInfo.GetVersionInfo(options.UnityExePath);
60 | var projectDirectory = Path.Combine(System.AppContext.BaseDirectory, "DummyProjects", "DummyProject-" + version.FileVersion);
61 | var commandLineArgs = new List
62 | {
63 | "-nographics",
64 | "-batchmode",
65 | "-logFile", Path.Combine(System.AppContext.BaseDirectory, "Log.txt")
66 | };
67 |
68 | if (version.FileMajorPart == 3)
69 | {
70 | commandLineArgs.Add("-executeMethod");
71 | commandLineArgs.Add(string.Join(".", typeof(FallbackLoader).FullName, nameof(FallbackLoader.Initialize)));
72 | }
73 |
74 | if (version.FileMajorPart >= 2018)
75 | commandLineArgs.Add("-noUpm");
76 |
77 | commandLineArgs.Add(Directory.Exists(projectDirectory) ? "-projectPath" : "-createProject");
78 | commandLineArgs.Add(projectDirectory);
79 |
80 | foreach (var process in Process.GetProcessesByName("Unity"))
81 | {
82 | using var mo = GetManagementObjectForProcess(process);
83 | var executablePath = mo.GetPropertyValue("ExecutablePath") as string ?? string.Empty;
84 | var commandLine = mo.GetPropertyValue("CommandLine") as string ?? string.Empty;
85 |
86 | if (executablePath.Equals(options.UnityExePath, StringComparison.OrdinalIgnoreCase) &&
87 | commandLine.Contains(EscapeArgument(projectDirectory)))
88 | {
89 | Console.WriteLine("Terminating orphaned editor process {0}...", process.Id);
90 | process.Kill();
91 | }
92 | }
93 |
94 | STARTUPINFOW si;
95 | PROCESS_INFORMATION pi;
96 |
97 | fixed (char* pAppName = options.UnityExePath)
98 | fixed (char* pCmdLine = CreateCommandLine(commandLineArgs))
99 | {
100 | if (!CreateProcessW((ushort*)pAppName, (ushort*)pCmdLine, null, null, true, CREATE_SUSPENDED, null, null, &si, &pi))
101 | {
102 | Console.WriteLine("Failed to start Unity process.");
103 | return;
104 | }
105 | }
106 |
107 | // The handle we get from CreateProcessW is only valid for this process,
108 | // so we need to duplicate the handle to pass it to the Unity editor process.
109 | HANDLE hThread;
110 | DuplicateHandle(GetCurrentProcess(), pi.hThread, pi.hProcess, &hThread, 0, true, DUPLICATE_SAME_ACCESS);
111 | CloseHandle(pi.hProcess);
112 | CloseHandle(pi.hThread);
113 |
114 | var config = Path.Combine(AppContext.BaseDirectory, "TypeTreeDumper.Bootstrapper.runtimeconfig.json");
115 |
116 | if (!File.Exists(config))
117 | {
118 | config = Path.GetTempFileName();
119 | File.WriteAllText(config, DefaultRuntimeConfig);
120 | }
121 |
122 | try
123 | {
124 | using var runtime = new RemoteRuntime((int)pi.dwProcessId);
125 | runtime.Initialize(config);
126 | runtime.Invoke(((Delegate)EntryPoint.Main).Method, new EntryPointArgs
127 | {
128 | OutputPath = Path.GetFullPath(options.OutputDirectory ?? Path.Combine(System.AppContext.BaseDirectory, "Output")),
129 | ProjectPath = projectDirectory,
130 | Verbose = options.Verbose,
131 | Silent = options.Silent,
132 | Debug = options.Debug,
133 | ThreadHandle = (ulong)hThread,
134 | });
135 |
136 | Process.GetProcessById((int)pi.dwProcessId).WaitForExit();
137 | }
138 | catch (COMException)
139 | {
140 | // Process killed after completion
141 | }
142 | }
143 |
144 | static ManagementBaseObject GetManagementObjectForProcess(Process process)
145 | {
146 | var query = $"select * from Win32_Process where ProcessId = {process.Id}";
147 | using var searcher = new ManagementObjectSearcher(query);
148 | using var processes = searcher.Get();
149 | return processes.OfType().FirstOrDefault();
150 | }
151 |
152 | static string EscapeArgument(string arg)
153 | {
154 | if (string.IsNullOrWhiteSpace(arg))
155 | return '"' + (arg ?? string.Empty) + '"';
156 |
157 | if (arg.Contains('.') || arg.Contains(' ') || arg.Contains('"'))
158 | return '"' + arg.Replace("\"", "\\\"") + '"';
159 |
160 | return arg;
161 | }
162 |
163 | static string CreateCommandLine(List args)
164 | {
165 | var sb = new StringBuilder();
166 |
167 | for (int i = 0; i < args.Count; i++)
168 | {
169 | if (i > 0)
170 | sb.Append(' ');
171 |
172 | sb.Append(EscapeArgument(args[i]));
173 | }
174 |
175 | return sb.ToString();
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/TypeTreeDumper.Bootstrapper/TypeTreeDumper.Bootstrapper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0-windows
5 | latest
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/TypeTreeDumper.Loader/FallbackLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TypeTreeDumper
5 | {
6 | public static class FallbackLoader
7 | {
8 | public const string CallbackAddressName = "CALLBACK_ADDRESS";
9 |
10 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
11 | public delegate void CallbackDelegate();
12 |
13 | public static void Initialize()
14 | {
15 | var address = new IntPtr(long.Parse(Environment.GetEnvironmentVariable(CallbackAddressName)));
16 | Marshal.GetDelegateForFunctionPointer(address, typeof(CallbackDelegate)).DynamicInvoke();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TypeTreeDumper.Loader/TypeTreeDumper.Loader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net20
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TypeTreeDumper.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeTreeDumper.Bootstrapper", "TypeTreeDumper.Bootstrapper\TypeTreeDumper.Bootstrapper.csproj", "{518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeTreeDumper", "TypeTreeDumper\TypeTreeDumper.csproj", "{CCF692AC-723D-4E90-86C8-66CDD6FD4027}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnityInterop", "UnityInterop\UnityInterop.csproj", "{42AE78A9-4BF2-4E1D-966D-08A0728A4C42}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeTreeDumper.Loader", "TypeTreeDumper.Loader\TypeTreeDumper.Loader.csproj", "{3F065ACF-8723-4AAD-A916-A11621AECA96}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6ECA25EE-9149-4434-A933-287DCC7E918D}"
15 | ProjectSection(SolutionItems) = preProject
16 | .gitignore = .gitignore
17 | GenDia2Lib.bat = GenDia2Lib.bat
18 | LICENSE = LICENSE
19 | README.md = README.md
20 | EndProjectSection
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|Any CPU = Release|Any CPU
28 | Release|x64 = Release|x64
29 | Release|x86 = Release|x86
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|x64.ActiveCfg = Debug|Any CPU
35 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|x64.Build.0 = Debug|Any CPU
36 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|x86.ActiveCfg = Debug|Any CPU
37 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Debug|x86.Build.0 = Debug|Any CPU
38 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|x64.ActiveCfg = Release|Any CPU
41 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|x64.Build.0 = Release|Any CPU
42 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|x86.ActiveCfg = Release|Any CPU
43 | {518C83DF-C74E-4AD3-8A14-B4EA9C636CF2}.Release|x86.Build.0 = Release|Any CPU
44 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|x64.ActiveCfg = Debug|Any CPU
47 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|x64.Build.0 = Debug|Any CPU
48 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|x86.ActiveCfg = Debug|Any CPU
49 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Debug|x86.Build.0 = Debug|Any CPU
50 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|x64.ActiveCfg = Release|Any CPU
53 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|x64.Build.0 = Release|Any CPU
54 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|x86.ActiveCfg = Release|Any CPU
55 | {CCF692AC-723D-4E90-86C8-66CDD6FD4027}.Release|x86.Build.0 = Release|Any CPU
56 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|x64.ActiveCfg = Debug|Any CPU
59 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|x64.Build.0 = Debug|Any CPU
60 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|x86.ActiveCfg = Debug|Any CPU
61 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Debug|x86.Build.0 = Debug|Any CPU
62 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
63 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|Any CPU.Build.0 = Release|Any CPU
64 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|x64.ActiveCfg = Release|Any CPU
65 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|x64.Build.0 = Release|Any CPU
66 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|x86.ActiveCfg = Release|Any CPU
67 | {42AE78A9-4BF2-4E1D-966D-08A0728A4C42}.Release|x86.Build.0 = Release|Any CPU
68 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|Any CPU.Build.0 = Debug|Any CPU
70 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|x64.ActiveCfg = Debug|Any CPU
71 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|x64.Build.0 = Debug|Any CPU
72 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|x86.ActiveCfg = Debug|Any CPU
73 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Debug|x86.Build.0 = Debug|Any CPU
74 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|Any CPU.ActiveCfg = Release|Any CPU
75 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|Any CPU.Build.0 = Release|Any CPU
76 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|x64.ActiveCfg = Release|Any CPU
77 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|x64.Build.0 = Release|Any CPU
78 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|x86.ActiveCfg = Release|Any CPU
79 | {3F065ACF-8723-4AAD-A916-A11621AECA96}.Release|x86.Build.0 = Release|Any CPU
80 | EndGlobalSection
81 | GlobalSection(SolutionProperties) = preSolution
82 | HideSolutionNode = FALSE
83 | EndGlobalSection
84 | GlobalSection(ExtensibilityGlobals) = postSolution
85 | SolutionGuid = {4547E4A0-4D20-4D9C-835D-5E7F2DB73ECC}
86 | EndGlobalSection
87 | EndGlobal
88 |
--------------------------------------------------------------------------------
/TypeTreeDumper/AsmSymbolResolver.cs:
--------------------------------------------------------------------------------
1 | using AsmResolver.PE.File;
2 | using AsmResolver.Symbols.Pdb;
3 | using AsmResolver.Symbols.Pdb.Records;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.Diagnostics.CodeAnalysis;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Text.RegularExpressions;
11 | using Unity;
12 |
13 | namespace TypeTreeDumper;
14 | internal class AsmSymbolResolver : SymbolResolver
15 | {
16 | readonly ProcessModule module;
17 |
18 | readonly Dictionary cache = new();
19 |
20 | IntPtr BaseAddress => module.BaseAddress;
21 |
22 | public AsmSymbolResolver(ProcessModule module)
23 | {
24 | this.module = module;
25 |
26 | if (!TryGetPaths(module, out string exePath, out string pdbPath))
27 | {
28 | throw new ArgumentException("Module paths could not be determined.", nameof(module));
29 | }
30 |
31 | //This operates on an unsafe assumption that pdb segments match one-to-one with pe sections.
32 | //While this has been historically true, it is not guaranteed.
33 | uint[] sectionOffsets = PEFile.FromModuleBaseAddress(BaseAddress).Sections.Select(s => s.Rva).ToArray();
34 |
35 | foreach (ICodeViewSymbol symbol in PdbImage.FromFile(pdbPath).Symbols)
36 | {
37 | if (symbol is PublicSymbol publicSymbol)
38 | {
39 | if (publicSymbol.SegmentIndex == sectionOffsets.Length + 1)
40 | {
41 | //Ignore these rare symbols. They are just for cfguard.
42 | }
43 | else
44 | {
45 | cache.TryAdd(publicSymbol.Name, publicSymbol.Offset + sectionOffsets[publicSymbol.SegmentIndex - 1]);
46 | }
47 | }
48 | }
49 | }
50 |
51 | public override IEnumerable FindSymbolsMatching(Regex expression)
52 | {
53 | foreach (string name in cache.Keys)
54 | {
55 | if (expression.IsMatch(name))
56 | {
57 | yield return name;
58 | }
59 | }
60 | }
61 |
62 | protected override unsafe void* GetAddressOrZero(string name)
63 | {
64 | return cache.TryGetValue(name, out uint offset) ? (void*)(BaseAddress + (nint)offset) : default;
65 | }
66 |
67 | private static bool TryGetPaths(ProcessModule module, [MaybeNullWhen(false)] out string exePath, [MaybeNullWhen(false)] out string pdbPath)
68 | {
69 | exePath = module.FileName;
70 | if (string.IsNullOrEmpty(exePath))
71 | {
72 | pdbPath = default;
73 | return false;
74 | }
75 |
76 | string pathWithoutExtension = Path.ChangeExtension(exePath, null);
77 | foreach (string suffix in new string[] { ".pdb", "_x64.pdb" })
78 | {
79 | string path = pathWithoutExtension + suffix;
80 | if (File.Exists(path))
81 | {
82 | pdbPath = path;
83 | return true;
84 | }
85 | }
86 |
87 | pdbPath = default;
88 | return false;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/TypeTreeDumper/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public static class ConsoleLogger
6 | {
7 | ///
8 | /// Ignore everything but errors
9 | ///
10 | private static bool Silent { get; set; }
11 | private static bool Verbose { get; set; }
12 |
13 | internal static void Initialize(bool silent, bool verbose)
14 | {
15 | Logger.InfoLog += Info;
16 | Logger.VerbLog += Verb;
17 | Logger.ErrorLog += Error;
18 | Silent = silent;
19 | Verbose = verbose;
20 | Logger.Info("Console Logging Initialized");
21 | }
22 |
23 | private static void Info(string message)
24 | {
25 | if (!Silent) Console.WriteLine(message);
26 | }
27 | private static void Verb(string message)
28 | {
29 | if (!Silent && Verbose) Console.WriteLine(message);
30 | }
31 | private static void Error(string message)
32 | {
33 | Console.Error.WriteLine(message);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/TypeTreeDumper/DiaSourceFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 | using TerraFX.Interop.Windows;
5 | using static TerraFX.Interop.Windows.Windows;
6 |
7 | namespace TypeTreeDumper
8 | {
9 | static unsafe class DiaSourceFactory
10 | {
11 | public static HRESULT CreateDiaSource(IDiaDataSource** ppvObject)
12 | {
13 | return CreateInstance(__uuidof(), __uuidof(), (void**)ppvObject);
14 | }
15 |
16 | public static HRESULT CreateInstance(Guid* rclsid, Guid* riid, void** ppvObject)
17 | {
18 | using var factory = new ComPtr();
19 | HRESULT hr = DllGetClassObject(rclsid, __uuidof(), (void**)factory.GetAddressOf());
20 |
21 | if (hr.FAILED)
22 | return hr;
23 |
24 | return factory.Get()->CreateInstance(null, riid, ppvObject);
25 | }
26 |
27 | [DllImport("msdia", ExactSpelling = true)]
28 | public static extern HRESULT DllGetClassObject(Guid* rclsid, Guid* riid, void** ppv);
29 |
30 | [DllImport("msdia", ExactSpelling = true)]
31 | public static extern HRESULT DllCanUnloadNow();
32 |
33 | static DiaSourceFactory()
34 | {
35 | NativeLibrary.SetDllImportResolver(typeof(DiaSourceFactory).Assembly, DiaDllResolver);
36 | }
37 |
38 | static IntPtr DiaDllResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
39 | {
40 | if (libraryName != "msdia")
41 | return NativeLibrary.Load(libraryName, assembly, searchPath);
42 |
43 | if (Environment.Is64BitProcess)
44 | return NativeLibrary.Load("msdia140_amd64.dll", assembly, searchPath);
45 | else
46 | return NativeLibrary.Load("msdia140.dll", assembly, searchPath);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/TypeTreeDumper/DiaSymbolResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Diagnostics;
4 | using System.Collections.Generic;
5 | using System.Collections.Concurrent;
6 | using System.Text.RegularExpressions;
7 | using TerraFX.Interop.Windows;
8 | using Unity;
9 |
10 | namespace TypeTreeDumper
11 | {
12 | public unsafe class DiaSymbolResolver : SymbolResolver
13 | {
14 | readonly ThreadLocal> session;
15 |
16 | readonly ProcessModule module;
17 |
18 | readonly ConcurrentDictionary cache;
19 |
20 | public DiaSymbolResolver(ProcessModule module)
21 | {
22 | session = new ThreadLocal>(CreateSession);
23 | this.module = module;
24 | cache = new ConcurrentDictionary();
25 | }
26 |
27 | public override IEnumerable FindSymbolsMatching(Regex expression)
28 | {
29 | var options = NameSearchOptions.RegularExpression;
30 |
31 | if (expression.Options.HasFlag(RegexOptions.IgnoreCase))
32 | options |= NameSearchOptions.CaseInsensitive;
33 |
34 | using ComPtr globalScope = default;
35 | using ComPtr enumSymbols = default;
36 | int count = InitializeAndGetCount();
37 |
38 | for (int i = 0; i < count; i++)
39 | {
40 | yield return GetSymbolName(i);
41 | }
42 |
43 | unsafe int InitializeAndGetCount()
44 | {
45 | session.Value.Get()->get_globalScope(globalScope.GetAddressOf());
46 |
47 | fixed (char* pExpression = expression.ToString())
48 | globalScope.Get()->findChildren(SymTagEnum.SymTagPublicSymbol, (ushort*)pExpression, (uint)options, enumSymbols.GetAddressOf());
49 |
50 | int count;
51 | enumSymbols.Get()->get_Count(&count);
52 | return count;
53 | }
54 |
55 | unsafe string GetSymbolName(int index)
56 | {
57 | using ComPtr symbol = default;
58 | enumSymbols.Get()->Item((uint)index, symbol.GetAddressOf());
59 | ushort* name;
60 | symbol.Get()->get_name(&name);
61 | return new string((char*)name);
62 | }
63 | }
64 |
65 | protected override void* GetAddressOrZero(string name)
66 | {
67 | if (cache.TryGetValue(name, out IntPtr address))
68 | return (void*)address;
69 |
70 | using ComPtr globalScope = default;
71 | using ComPtr enumSymbols = default;
72 | session.Value.Get()->get_globalScope(globalScope.GetAddressOf());
73 |
74 | fixed (char* pName = name)
75 | globalScope.Get()->findChildren(SymTagEnum.SymTagPublicSymbol, (ushort*)pName, 0, enumSymbols.GetAddressOf());
76 |
77 | int count;
78 | enumSymbols.Get()->get_Count(&count);
79 |
80 | if (count == 0)
81 | return null;
82 |
83 | uint rva;
84 | using ComPtr symbol = default;
85 | enumSymbols.Get()->Item(0, symbol.GetAddressOf());
86 | symbol.Get()->get_relativeVirtualAddress(&rva);
87 |
88 | address = new IntPtr((nint)module.BaseAddress + rva);
89 | cache.TryAdd(name, address);
90 | return (void*)address;
91 | }
92 |
93 | ComPtr CreateSession()
94 | {
95 | IDiaDataSource* source;
96 | ComPtr session = default;
97 | DiaSourceFactory.CreateDiaSource(&source);
98 |
99 | fixed (char* pFileName = module.FileName)
100 | source->loadDataForExe((ushort*)pFileName, null, null);
101 |
102 | source->openSession(session.GetAddressOf());
103 | return session;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/TypeTreeDumper/Dumper.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using Unity;
8 |
9 | namespace TypeTreeDumper
10 | {
11 | static class Dumper
12 | {
13 | static ExportOptions Options = new();
14 |
15 | public static void Execute(UnityEngine engine, ExportOptions options, DumperEngine dumperEngine)
16 | {
17 | Options = options;
18 | Logger.Info($"Starting export. UnityVersion {engine.Version}.");
19 | Directory.CreateDirectory(Options.OutputDirectory);
20 | TransferInstructionFlags releaseFlags = Options.TransferFlags | TransferInstructionFlags.SerializeGameRelease;
21 | TransferInstructionFlags editorFlags = Options.TransferFlags & (~TransferInstructionFlags.SerializeGameRelease);
22 | var info = UnityInfo.Create(engine, releaseFlags, editorFlags);
23 | if (options.ExportClassesJson)
24 | {
25 | ExportClassesJson(info);
26 | }
27 | if (options.ExportTextDump)
28 | {
29 | ExportRTTI(info);
30 | ExportStructDump(info, "structs.dump", true);
31 | ExportStructDump(info, "editor_structs.dump", false);
32 | ExportInfoJson(info);
33 | }
34 | if (options.ExportBinaryDump)
35 | {
36 | if (engine.Version >= UnityVersion.Unity5_0)
37 | {
38 | ExportStringData(engine.CommonString);
39 | }
40 | ExportStructData(engine, "structs.dat", releaseFlags);
41 | ExportStructData(engine, "editor_structs.dat", editorFlags);
42 | }
43 | dumperEngine.InvokeExportCompleted(engine, options);
44 | Logger.Info("Success");
45 |
46 | // Kill the process since we're done and don't want to wait for Unity to exit on its own.
47 | // Unity 6.2+ requires this.
48 | Process.GetCurrentProcess().Kill();
49 | }
50 |
51 | static void ExportRTTI(UnityInfo info)
52 | {
53 | Logger.Info("Writing RTTI...");
54 | using var tw = new StreamWriter(Path.Combine(Options.OutputDirectory, "RTTI.dump"));
55 | foreach (var type in info.Classes.OrderBy(x => x.TypeID))
56 | {
57 | tw.WriteLine($"PersistentTypeID {type.TypeID}");
58 | tw.WriteLine($" Name {type.Name}");
59 | tw.WriteLine($" Namespace {type.Namespace}");
60 | tw.WriteLine($" Module {type.Module}");
61 | tw.WriteLine($" Base {type.Base ?? ""}");
62 | tw.WriteLine($" DescendantCount {type.DescendantCount}");
63 | tw.WriteLine($" IsAbstract {type.IsAbstract}");
64 | tw.WriteLine($" IsSealed {type.IsSealed}");
65 | tw.WriteLine($" IsStripped {type.IsStripped}");
66 | tw.WriteLine($" IsEditorOnly {type.IsEditorOnly}");
67 | tw.WriteLine();
68 | }
69 | }
70 |
71 | static void ExportStringData(CommonString strings)
72 | {
73 | byte[] data = strings.GetData().ToArray();
74 | if (data.Length == 0)
75 | return;
76 |
77 | Logger.Info("Writing common string buffer...");
78 | File.WriteAllBytes(Path.Combine(Options.OutputDirectory, "strings.dat"), data);
79 | }
80 |
81 | static void ExportClassesJson(UnityInfo info)
82 | {
83 | Logger.Info("Writing classes.json...");
84 | using var tw = new StreamWriter(Path.Combine(Options.OutputDirectory, "classes.json"));
85 | tw.WriteLine("{");
86 |
87 | IEnumerable entries = from type in info.Classes.OrderBy(x => (int)x.TypeID) select $" \"{(int)type.TypeID}\": \"{type.Name}\"";
88 | var json = string.Join(',' + tw.NewLine, entries);
89 |
90 | tw.WriteLine(json);
91 | tw.WriteLine("}");
92 | }
93 |
94 | unsafe static void ExportStructData(UnityEngine engine, string fileName, TransferInstructionFlags flags)
95 | {
96 | Logger.Info("Writing structure information...");
97 | using var bw = new BinaryWriter(File.OpenWrite(Path.Combine(Options.OutputDirectory, fileName)));
98 |
99 | bw.Write(Encoding.UTF8.GetBytes(engine.Version.ToString()));
100 | bw.Write((byte)0);
101 |
102 | bw.Write((int)RuntimePlatform.WindowsEditor);
103 | bw.Write((byte)1); // hasTypeTrees
104 |
105 | var countPosition = (int)bw.BaseStream.Position;
106 | var typeCount = 0;
107 | //Later will be overwritten with actual type count
108 | bw.Write(typeCount);
109 |
110 | Logger.Verb("Writing runtime types...");
111 | foreach(var type in engine.RuntimeTypes.ToArray().OrderBy(x => (int)x.PersistentTypeID))
112 | {
113 | var iter = type;
114 |
115 | Logger.Verb("[{0}] Child: {1}::{2}, {3}, {4}",
116 | typeCount,
117 | type.Namespace,
118 | type.Name,
119 | type.Module,
120 | type.PersistentTypeID
121 | );
122 |
123 | Logger.Verb("[{0}] Getting base type...", typeCount);
124 | while (iter.IsAbstract)
125 | {
126 | if (iter.Base == null)
127 | break;
128 |
129 | iter = iter.Base;
130 | }
131 |
132 | Logger.Verb("[{0}] Base: {1}::{2}, {3}, {4}",
133 | typeCount,
134 | iter.Namespace,
135 | iter.Name,
136 | iter.Module,
137 | iter.PersistentTypeID
138 | );
139 |
140 | Logger.Verb("[{0}] Producing native object...", typeCount);
141 | var obj = engine.ObjectFactory.GetOrProduce(iter);
142 |
143 | if (obj == null)
144 | continue;
145 |
146 | Logger.Verb("[{0}] Produced object {1}. Persistent = {2}.", typeCount, obj.InstanceID, obj.IsPersistent);
147 | Logger.Verb("[{0}] Generating type tree...", typeCount);
148 | var tree = engine.TypeTreeFactory.GetTypeTree(obj, flags);
149 |
150 | Logger.Verb("[{0}] Getting GUID...", typeCount);
151 | bw.Write((int)iter.PersistentTypeID);
152 | for (int j = 0, n = iter.PersistentTypeID < 0 ? 0x20 : 0x10; j < n; ++j)
153 | bw.Write((byte)0);
154 |
155 | TypeTreeUtility.CreateBinaryDump(tree, bw);
156 | typeCount++;
157 | }
158 |
159 | bw.Seek(countPosition, SeekOrigin.Begin);
160 | bw.Write(typeCount);
161 | }
162 |
163 | static void ExportStructDump(UnityInfo info, string fileName, bool isRelease)
164 | {
165 | Logger.Info("Writing structure information dump...");
166 | using var tw = new StreamWriter(Path.Combine(Options.OutputDirectory, fileName));
167 |
168 | int typeCount = 0;
169 | foreach (var type in info.Classes.OrderBy(x => (int)x.TypeID))
170 | {
171 | var iter = type;
172 | var inheritance = string.Empty;
173 |
174 | Logger.Verb("[{0}] Child: {1}::{2}, {3}, {4}",
175 | typeCount,
176 | type.Namespace,
177 | type.Name,
178 | type.Module,
179 | type.TypeID
180 | );
181 |
182 | Logger.Verb("[{0}] Getting base type...", typeCount);
183 | while (true)
184 | {
185 | inheritance += iter.Name;
186 |
187 | if (string.IsNullOrEmpty(iter.Base))
188 | break;
189 |
190 | inheritance += " <- ";
191 | iter = info.Classes.Single(c => c.Name == iter.Base);
192 | }
193 |
194 | tw.WriteLine("\n// classID{{{0}}}: {1}", (int)type.TypeID, inheritance);
195 | iter = type;
196 |
197 | while (iter.IsAbstract)
198 | {
199 | tw.WriteLine("// {0} is abstract", iter.Name);
200 |
201 | if (string.IsNullOrEmpty(iter.Base))
202 | break;
203 |
204 | iter = info.Classes.Single(c => c.Name == iter.Base);
205 | }
206 |
207 | Logger.Verb("[{0}] Base: {1}::{2}, {3}, {4}",
208 | typeCount,
209 | iter.Namespace,
210 | iter.Name,
211 | iter.Module,
212 | iter.TypeID
213 | );
214 |
215 | var tree = isRelease ? iter.ReleaseRootNode : iter.EditorRootNode;
216 | if(tree != null)
217 | TypeTreeUtility.CreateTextDump(tree, tw);
218 |
219 | typeCount++;
220 | }
221 | }
222 |
223 | static void ExportInfoJson(UnityInfo info)
224 | {
225 | Logger.Info("Writing information json...");
226 | using var sw = new StreamWriter(Path.Combine(Options.OutputDirectory, "info.json"));
227 | using var jw = new JsonTextWriter(sw) { Indentation = 1, IndentChar = '\t' };
228 | new JsonSerializer { Formatting = Formatting.Indented }.Serialize(jw, info);
229 | }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/TypeTreeDumper/DumperEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Unity;
3 |
4 | namespace TypeTreeDumper
5 | {
6 | internal class DumperEngine : IDumperEngine
7 | {
8 | public event Action OnExportCompleted;
9 |
10 | internal void InvokeExportCompleted(UnityEngine engine, ExportOptions options) => OnExportCompleted?.Invoke(engine, options);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TypeTreeDumper/EntryPoint.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using System.Diagnostics;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.CompilerServices;
7 | using System.Text.RegularExpressions;
8 | using EasyHook;
9 | using Unity;
10 | using TerraFX.Interop.Windows;
11 | using static TerraFX.Interop.Windows.Windows;
12 |
13 | namespace TypeTreeDumper
14 | {
15 | public static unsafe class EntryPoint
16 | {
17 | static ProcessModule module;
18 |
19 | static SymbolResolver resolver;
20 |
21 | static event Action OnEngineInitialized;
22 |
23 | static string OutputPath;
24 |
25 | static string ProjectPath;
26 |
27 | static FileVersionInfo VersionInfo;
28 |
29 | static LocalHook AfterEverythingLoadedDetour;
30 |
31 | static LocalHook PlayerInitEngineNoGraphicsDetour;
32 |
33 | static LocalHook ValidateDatesDetour;
34 |
35 | static LocalHook InitializePackageManagerDetour;
36 |
37 | //This can be removed. I initially had it in for debugging, but it never got called.
38 | static LocalHook AssertImplementationDetour;
39 |
40 | static void AttachToParentConsole()
41 | {
42 | FreeConsole();
43 | AttachConsole(ATTACH_PARENT_PROCESS);
44 | Console.SetIn(new StreamReader(Console.OpenStandardInput()));
45 | Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
46 | Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true });
47 | }
48 |
49 | static ProcessModule GetUnityModule(Process process)
50 | {
51 | if (TryGetModule(process, "Unity.dll", out var module))
52 | return module;
53 |
54 | return process.MainModule;
55 | }
56 |
57 | static bool TryGetModule(Process process, string name, out ProcessModule module)
58 | {
59 | foreach (ProcessModule entry in process.Modules)
60 | {
61 | if (entry.ModuleName == name)
62 | {
63 | module = entry;
64 | return true;
65 | }
66 | }
67 |
68 | module = null;
69 | return false;
70 | }
71 |
72 | public static void Main(EntryPointArgs args)
73 | {
74 | if (args.Debug)
75 | {
76 | Debugger.Launch();
77 | }
78 |
79 | try
80 | {
81 | VersionInfo = FileVersionInfo.GetVersionInfo(Environment.ProcessPath);
82 |
83 | // If we're on a version of Unity after the editor code was split into a
84 | // separate dynamic library, it won't be loaded at this point. We need to
85 | // access stuff inside it, so we try to load it here ahead of time.
86 | NativeLibrary.TryLoad("Unity.dll", out _);
87 |
88 | // Can cause 2017.1 & 2017.2 to hang, cause is currently unknown but may be
89 | // related to the engine trying to attach to the package manager.
90 | if (!(VersionInfo.FileMajorPart == 2017 && VersionInfo.FileMinorPart < 3))
91 | AttachToParentConsole();
92 |
93 | ConsoleLogger.Initialize(args.Silent, args.Verbose);
94 |
95 | if (!(VersionInfo.FileMajorPart == 2017 && VersionInfo.FileMinorPart < 3))
96 | AttachToParentConsole();
97 |
98 | module = GetUnityModule(Process.GetCurrentProcess());
99 | OutputPath = args.OutputPath;
100 | ProjectPath = args.ProjectPath;
101 | resolver = new DiaSymbolResolver(module);
102 | void* address;
103 |
104 | if (VersionInfo.FileMajorPart == 2017)
105 | {
106 | if (resolver.TryResolve($"?Initialize@Api@PackageManager@@I{NameMangling.Ptr64}AAXXZ", out address))
107 | {
108 | InitializePackageManagerDetour = LocalHook.CreateUnmanaged((IntPtr)address, (IntPtr)(delegate* unmanaged[Cdecl])&InitializePackageManager, IntPtr.Zero);
109 | InitializePackageManagerDetour.ThreadACL.SetExclusiveACL(Array.Empty());
110 | }
111 | }
112 |
113 | if (VersionInfo.FileMajorPart == 3)
114 | {
115 | InitializeFallbackLoader();
116 | }
117 | else if (resolver.TryResolveFirstMatch(new Regex(Regex.Escape("?AfterEverythingLoaded@Application@") + "*"), out address))
118 | {
119 | AfterEverythingLoadedDetour = LocalHook.CreateUnmanaged((IntPtr)address, (IntPtr)(delegate* unmanaged[Cdecl])&AfterEverythingLoaded, IntPtr.Zero);
120 | AfterEverythingLoadedDetour.ThreadACL.SetExclusiveACL(Array.Empty());
121 | }
122 | else
123 | {
124 | address = resolver.ResolveFirstMatch(
125 | new Regex(Regex.Escape("?PlayerInitEngineNoGraphics@") + "*"),
126 | new Regex(Regex.Escape("?InitializeEngineNoGraphics@") + "*")
127 | );
128 | PlayerInitEngineNoGraphicsDetour = LocalHook.CreateUnmanaged((IntPtr)address, (IntPtr)(delegate* unmanaged[Cdecl])&PlayerInitEngineNoGraphics, IntPtr.Zero);
129 | PlayerInitEngineNoGraphicsDetour.ThreadACL.SetExclusiveACL(Array.Empty());
130 | }
131 |
132 | // Work around Unity 4.0 to 4.3 licensing bug
133 | if (VersionInfo.FileMajorPart == 4 && VersionInfo.FileMinorPart <= 3)
134 | {
135 | address = resolver.Resolve($"?ValidateDates@LicenseManager@@QAEHP{NameMangling.Ptr64}AVDOMDocument@xercesc_3_1@@@Z");
136 | ValidateDatesDetour = LocalHook.CreateUnmanaged((IntPtr)address, (IntPtr)(delegate* unmanaged[Thiscall])&ValidateDates, IntPtr.Zero);
137 | ValidateDatesDetour.ThreadACL.SetExclusiveACL(Array.Empty());
138 | }
139 |
140 | if (resolver.TryResolve(@"?AssertImplementation@@YA_NHPEBDHH0@Z", out void* assertAddress))
141 | {
142 | AssertImplementationDetour = LocalHook.CreateUnmanaged((IntPtr)assertAddress, (IntPtr)(delegate* unmanaged[Cdecl])&AssertImplementation, IntPtr.Zero);
143 | AssertImplementationDetour.ThreadACL.SetExclusiveACL(Array.Empty());
144 | }
145 |
146 | OnEngineInitialized += PluginManager.LoadPlugins;
147 | OnEngineInitialized += ExecuteDumper;
148 | ResumeThread((HANDLE)args.ThreadHandle);
149 | Thread.Sleep(Timeout.Infinite);
150 | }
151 | catch (Exception ex)
152 | {
153 | Logger.Error(ex);
154 | throw;
155 | }
156 | }
157 |
158 | static ProcessModule FindProcessModule(Regex regex)
159 | {
160 | foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
161 | {
162 | if (regex.IsMatch(module.ModuleName))
163 | return module;
164 | }
165 |
166 | throw new MissingModuleException(regex.ToString());
167 | }
168 |
169 | static void ExecuteDumper()
170 | {
171 | Logger.Info("Executing Dumper");
172 | UnityVersion version;
173 | delegate* unmanaged[Cdecl] GetUnityVersion;
174 |
175 | var dumperEngine = new DumperEngine();
176 | PluginManager.InitializePlugins(dumperEngine);
177 |
178 | if (resolver.TryResolve($"?GameEngineVersion@PlatformWrapper@UnityEngine@@SAP{NameMangling.Ptr64}BDXZ", out *(void**)&GetUnityVersion))
179 | {
180 | var ParseUnityVersion = (delegate* unmanaged[Cdecl])resolver.Resolve(
181 | $"??0UnityVersion@@Q{NameMangling.Ptr64}AA@P{NameMangling.Ptr64}BD@Z",
182 | $"??0UnityVersion@@QAE@P{NameMangling.Ptr64}BD@Z"
183 | );
184 |
185 | ParseUnityVersion(&version, GetUnityVersion());
186 | }
187 | else
188 | {
189 | *(void**)&GetUnityVersion = resolver.Resolve(
190 | $"?Application_Get_Custom_PropUnityVersion@@YAP{NameMangling.Ptr64}AUMonoString@@XZ",
191 | $"?Application_Get_Custom_PropUnityVersion@@YAP{NameMangling.Ptr64}AVScriptingBackendNativeStringPtrOpaque@@XZ"
192 | );
193 |
194 | var mono = FindProcessModule(new Regex("^mono", RegexOptions.IgnoreCase)).BaseAddress;
195 | var MonoStringToUTF8 = (delegate* unmanaged[Cdecl])NativeLibrary.GetExport(mono, "mono_string_to_utf8");
196 | version = new UnityVersion(Marshal.PtrToStringAnsi((IntPtr)MonoStringToUTF8(GetUnityVersion())));
197 | }
198 |
199 | Dumper.Execute(new UnityEngine(version, resolver), new ExportOptions(OutputPath), dumperEngine);
200 | }
201 |
202 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
203 | static void InitializePackageManager()
204 | {
205 | }
206 |
207 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvThiscall) })]
208 | static int ValidateDates(void* @this, void* param1)
209 | {
210 | Logger.Info("Validating dates");
211 | return 0;
212 | }
213 |
214 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl)})]
215 | static byte PlayerInitEngineNoGraphics(void* a, void* b)
216 | {
217 | ((delegate* unmanaged[Cdecl])PlayerInitEngineNoGraphicsDetour.HookBypassAddress)(a, b);
218 | HandleEngineInitialization();
219 | return 1;
220 | }
221 |
222 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
223 | static void AfterEverythingLoaded(void* app)
224 | {
225 | ((delegate* unmanaged[Cdecl])AfterEverythingLoadedDetour.HookBypassAddress)(app);
226 | HandleEngineInitialization();
227 | }
228 |
229 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
230 | static byte AssertImplementation(int instanceID, sbyte* filePtr, int line, int column, sbyte* messagePtr)
231 | {
232 | var mono = FindProcessModule(new Regex("^mono", RegexOptions.IgnoreCase)).BaseAddress;
233 | var MonoStringToUTF8 = (delegate* unmanaged[Cdecl])NativeLibrary.GetExport(mono, "mono_string_to_utf8");
234 | string file = Marshal.PtrToStringAnsi((IntPtr)MonoStringToUTF8(filePtr));
235 | string message = Marshal.PtrToStringAnsi((IntPtr)MonoStringToUTF8(messagePtr));
236 | Logger.Error($"Unity assertion for instance ID {instanceID}\n\tLocation: {file} at ({line}, {column})\n\tMessage: {message}");
237 | return ((delegate* unmanaged[Cdecl])AssertImplementationDetour.HookBypassAddress)(instanceID, filePtr, line, column, messagePtr);
238 | }
239 |
240 | static void HandleEngineInitialization()
241 | {
242 | try
243 | {
244 | AttachToParentConsole();
245 | OnEngineInitialized?.Invoke();
246 | }
247 | catch (Exception ex)
248 | {
249 | Logger.Error(ex);
250 | throw;
251 | }
252 | finally
253 | {
254 | Environment.Exit(0);
255 | }
256 | }
257 |
258 | [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
259 | static void FallbackLoaderCallback()
260 | {
261 | HandleEngineInitialization();
262 | }
263 |
264 | static void InitializeFallbackLoader()
265 | {
266 | Logger.Info("Initializing fallback loader...");
267 | var source = typeof(FallbackLoader).Assembly.Location;
268 | var destination = Path.Combine(ProjectPath, "Assets");
269 | var address = (delegate* unmanaged[Cdecl])&FallbackLoaderCallback;
270 |
271 | if (!Directory.Exists(destination))
272 | Directory.CreateDirectory(destination);
273 |
274 | File.Copy(source, Path.Combine(destination, Path.GetFileName(source)), overwrite: true);
275 | Environment.SetEnvironmentVariable(FallbackLoader.CallbackAddressName, new IntPtr(address).ToString());
276 | }
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/TypeTreeDumper/EntryPointArgs.cs:
--------------------------------------------------------------------------------
1 | using TerraFX.Interop.Windows;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public struct EntryPointArgs
6 | {
7 | public string OutputPath { get; set; }
8 | public string ProjectPath { get; set; }
9 | public bool Verbose { get; set; }
10 | public bool Silent { get; set; }
11 | public bool Debug { get; set; }
12 | public ulong ThreadHandle { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TypeTreeDumper/Examples/ExamplePostProcessingPlugin.cs:
--------------------------------------------------------------------------------
1 | using Unity;
2 |
3 | namespace TypeTreeDumper.Examples
4 | {
5 | public class ExamplePostProcessingPlugin : IDumperPlugin
6 | {
7 | public void Initialize(IDumperEngine dumper)
8 | {
9 | dumper.OnExportCompleted += PostProcessExport;
10 | }
11 |
12 | public bool TryGetInterface(UnityVersion version, out T result)
13 | {
14 | // This plugin doesn't provide any engine interfaces
15 | result = default;
16 | return false;
17 | }
18 |
19 | void PostProcessExport(UnityEngine engine, ExportOptions options)
20 | {
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TypeTreeDumper/ExportOptions.cs:
--------------------------------------------------------------------------------
1 | using Unity;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public class ExportOptions
6 | {
7 | public string OutputDirectory { get; set; }
8 |
9 | public TransferInstructionFlags TransferFlags { get; set; } = TransferInstructionFlags.SerializeGameRelease;
10 |
11 | public bool ExportTextDump { get; set; } = true;
12 |
13 | public bool ExportBinaryDump { get; set; } = true;
14 |
15 | public bool ExportClassesJson { get; set; } = true;
16 |
17 | public ExportOptions() { }
18 |
19 | public ExportOptions(string outputDirectory) => OutputDirectory = outputDirectory;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/TypeTreeDumper/IDumperEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Unity;
3 |
4 | namespace TypeTreeDumper
5 | {
6 | public interface IDumperEngine
7 | {
8 | event Action OnExportCompleted;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TypeTreeDumper/IDumperPlugin.cs:
--------------------------------------------------------------------------------
1 | using Unity;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public interface IDumperPlugin
6 | {
7 | void Initialize(IDumperEngine dumper);
8 | bool TryGetInterface(UnityVersion version, out T result);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TypeTreeDumper/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public static class Logger
6 | {
7 | public static event Action InfoLog;
8 | public static event Action VerbLog;
9 | public static event Action ErrorLog;
10 |
11 | public static void Info(object obj) => Info(obj.ToString());
12 | public static void Info(string message, params object[] parameters) => Info(string.Format(message, parameters));
13 | public static void Info(string message)
14 | {
15 | InfoLog?.Invoke(message);
16 | }
17 |
18 | public static void Verb(object obj) => Verb(obj.ToString());
19 | public static void Verb(string message, params object[] parameters) => Verb(string.Format(message, parameters));
20 | public static void Verb(string message)
21 | {
22 | VerbLog?.Invoke(message);
23 | }
24 |
25 | public static void Error(object obj) => Error(obj.ToString());
26 | public static void Error(string message, params object[] parameters) => Error(string.Format(message, parameters));
27 | public static void Error(string message)
28 | {
29 | ErrorLog?.Invoke(message);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/TypeTreeDumper/MissingModuleException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | public sealed class MissingModuleException : Exception
6 | {
7 | const string DefaultMessage = "The specified module could not be found.";
8 |
9 | public string ModuleName { get; }
10 |
11 | public MissingModuleException()
12 | : base(DefaultMessage)
13 | {
14 | }
15 |
16 | public MissingModuleException(Exception inner)
17 | : base(DefaultMessage, inner)
18 | {
19 | }
20 |
21 | public MissingModuleException(string moduleName)
22 | : base(DefaultMessage)
23 | {
24 | ModuleName = moduleName;
25 | }
26 |
27 | public MissingModuleException(string moduleName, Exception inner)
28 | : base(DefaultMessage, inner)
29 | {
30 | ModuleName = moduleName;
31 | }
32 |
33 | public MissingModuleException(string moduleName, string message)
34 | : base(message)
35 | {
36 | ModuleName = moduleName;
37 | }
38 |
39 | public MissingModuleException(string moduleName, string message, Exception inner)
40 | : base(message, inner)
41 | {
42 | ModuleName = moduleName;
43 | }
44 |
45 | public override string Message
46 | {
47 | get
48 | {
49 | var message = base.Message;
50 |
51 | if (!string.IsNullOrEmpty(message) && !string.IsNullOrEmpty(ModuleName))
52 | message += $" ({ModuleName})";
53 |
54 | return message;
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/TypeTreeDumper/NameSearchOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | [Flags]
6 | enum NameSearchOptions : uint
7 | {
8 | None,
9 | CaseSensitive = 1 << 0,
10 | CaseInsensitive = 1 << 1,
11 | FileNameExtension = 1 << 2,
12 | RegularExpression = 1 << 3,
13 | UndecoratedName = 1 << 4,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TypeTreeDumper/PluginManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace TypeTreeDumper
8 | {
9 | public static class PluginManager
10 | {
11 | public static string PluginDirectory { get; }
12 | internal static List Plugins { get; } = new List();
13 |
14 | static PluginManager()
15 | {
16 | PluginDirectory = Path.Combine(System.AppContext.BaseDirectory, "Plugins");
17 | Directory.CreateDirectory(PluginDirectory);
18 | }
19 |
20 | internal static void LoadPlugins()
21 | {
22 | Logger.Info("Loading plugins");
23 | foreach(string file in Directory.GetFiles(PluginDirectory, "*.dll"))
24 | {
25 | if(TryLoadAssembly(file, out Assembly assembly) && TryGetCustomAttributes(assembly, out IEnumerable attributes))
26 | {
27 | foreach (var attribute in attributes)
28 | {
29 | Type pluginType = attribute.PluginType;
30 | if (IsValidPluginType(pluginType))
31 | {
32 | try
33 | {
34 | Plugins.Add((IDumperPlugin)Activator.CreateInstance(pluginType));
35 | Logger.Info($"'{pluginType.Name}' loaded");
36 | }
37 | catch (Exception ex)
38 | {
39 | Logger.Error($"Error while instantiating {pluginType?.Name ?? "a plugin"}");
40 | Logger.Error(ex.ToString());
41 | }
42 | }
43 | }
44 | }
45 | }
46 | Logger.Info($"{Plugins.Count} plugins loaded");
47 | }
48 |
49 | internal static void InitializePlugins(IDumperEngine dumperEngine)
50 | {
51 | foreach(var plugin in Plugins)
52 | {
53 | plugin?.Initialize(dumperEngine);
54 | }
55 | }
56 |
57 | private static bool TryLoadAssembly(string path, out Assembly assembly)
58 | {
59 | try
60 | {
61 | assembly = Assembly.LoadFile(path);
62 | return true;
63 | }
64 | catch (Exception ex)
65 | {
66 | Logger.Error($"Error while loading the assembly at {path}");
67 | Logger.Error(ex.ToString());
68 | assembly = null;
69 | return false;
70 | }
71 | }
72 |
73 | private static bool TryGetCustomAttributes(Assembly assembly, out IEnumerable attributes) where T : Attribute
74 | {
75 | try
76 | {
77 | attributes = assembly.GetCustomAttributes();
78 | return true;
79 | }
80 | catch(Exception ex)
81 | {
82 | Logger.Error($"Error while getting the attributes for {assembly?.FullName ?? "an assembly"}");
83 | Logger.Error(ex.ToString());
84 | attributes = null;
85 | return false;
86 | }
87 | }
88 |
89 | private static bool IsValidPluginType(Type type)
90 | {
91 | return type != null
92 | && typeof(IDumperPlugin).IsAssignableFrom(type)
93 | && type.GetConstructors().Where(constructorInfo => constructorInfo.GetParameters().Length == 0).Any();
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/TypeTreeDumper/RegisterDumperPluginAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TypeTreeDumper
4 | {
5 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
6 | public sealed class RegisterDumperPluginAttribute : Attribute
7 | {
8 | public Type PluginType { get; }
9 |
10 | public RegisterDumperPluginAttribute(Type type) => PluginType = type;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TypeTreeDumper/TypeTreeDumper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | TypeTreeDumper
4 | net6.0-windows
5 | latest
6 | True
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/TypeTreeDumper/TypeTreeUtility.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Unity;
3 |
4 | namespace TypeTreeDumper
5 | {
6 | internal class TypeTreeUtility
7 | {
8 | internal static void CreateBinaryDump(TypeTree tree, BinaryWriter writer)
9 | {
10 | writer.Write(tree.Count);
11 | writer.Write(tree.StringBuffer.Count);
12 | for (int i = 0, n = tree.Count; i < n; i++)
13 | {
14 | var node = tree[i];
15 | writer.Write(node.Version);
16 | writer.Write(node.Level);
17 | writer.Write((byte)node.TypeFlags);
18 | writer.Write(node.TypeStrOffset);
19 | writer.Write(node.NameStrOffset);
20 | writer.Write(node.ByteSize);
21 | writer.Write(node.Index);
22 | writer.Write((uint)node.MetaFlag);
23 | }
24 | for (int i = 0, n = tree.StringBuffer.Count; i < n; i++)
25 | writer.Write(tree.StringBuffer[i]);
26 | }
27 |
28 | internal static void CreateTextDump(UnityNode node, StreamWriter writer)
29 | {
30 | for (int j = 0; j < node.Level; j++)
31 | writer.Write('\t');
32 | string type = node.TypeName;
33 | string name = node.Name;
34 | writer.WriteLine(string.Format("{0} {1} // ByteSize{{{2}}}, Index{{{3}}}, Version{{{4}}}, IsArray{{{5}}}, MetaFlag{{{6}}}",
35 | type,
36 | name,
37 | node.ByteSize.ToString("x"),
38 | node.Index.ToString("x"),
39 | node.Version.ToString("x"),
40 | node.TypeFlags.ToString("x"),
41 | node.MetaFlag.ToString("x")
42 | ));
43 |
44 | if (node.SubNodes != null)
45 | {
46 | for (int i = 0; i < node.SubNodes.Count; i++)
47 | {
48 | CreateTextDump(node.SubNodes[i], writer);
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/TypeTreeDumper/UnityClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Unity;
5 |
6 | namespace TypeTreeDumper
7 | {
8 | internal class UnityClass
9 | {
10 | public string Name { get; set; }
11 | public string Namespace { get; set; }
12 | public string FullName { get; set; }
13 | public string Module { get; set; }
14 | public int TypeID { get; set; }
15 | public string Base { get; set; }
16 | public List Derived { get; set; }
17 | public uint DescendantCount { get; set; }
18 | public int Size { get; set; }
19 | public uint TypeIndex { get; set; }
20 | public bool IsAbstract { get; set; }
21 | public bool IsSealed { get; set; }
22 | public bool IsEditorOnly { get; set; }
23 | public bool IsStripped { get; set; }
24 | public UnityNode EditorRootNode { get; set; }
25 | public UnityNode ReleaseRootNode { get; set; }
26 |
27 | public UnityClass() { }
28 | public UnityClass(RuntimeTypeInfo runtimeType)
29 | {
30 | Name = runtimeType.Name;
31 | Namespace = runtimeType.Namespace;
32 | FullName = runtimeType.FullName;
33 | Module = runtimeType.Module;
34 | TypeID = (int)runtimeType.PersistentTypeID;
35 | Base = runtimeType.Base?.Name ?? "";
36 | Derived = runtimeType.Derived.ConvertAll(d => d?.Name ?? "");
37 | DescendantCount = runtimeType.DescendantCount;
38 | Size = runtimeType.Size;
39 | TypeIndex = runtimeType.TypeIndex;
40 | IsAbstract = runtimeType.IsAbstract;
41 | IsSealed = runtimeType.IsSealed;
42 | IsEditorOnly = runtimeType.IsEditorOnly;
43 | IsStripped = runtimeType.IsStripped;
44 | }
45 |
46 | public static List MakeList(UnityEngine engine, TransferInstructionFlags releaseFlags, TransferInstructionFlags editorFlags)
47 | {
48 | var result = new List();
49 |
50 | foreach (var type in engine.RuntimeTypes.ToArray().OrderBy(x => (int)x.PersistentTypeID))
51 | {
52 | var next = new UnityClass(type);
53 |
54 | var iter = type;
55 | while (iter.IsAbstract)
56 | {
57 | if (iter.Base == null)
58 | break;
59 | else
60 | iter = iter.Base;
61 | }
62 |
63 | var obj = engine.ObjectFactory.GetOrProduce(iter);
64 |
65 | if (obj != null)
66 | {
67 | TypeTree editorTree = engine.TypeTreeFactory.GetTypeTree(obj, editorFlags);
68 | TypeTree releaseTree = engine.TypeTreeFactory.GetTypeTree(obj, releaseFlags);
69 |
70 | next.EditorRootNode = CreateRootNode(editorTree);
71 | next.ReleaseRootNode = CreateRootNode(releaseTree);
72 | }
73 |
74 | result.Add(next);
75 | }
76 |
77 | return result;
78 | }
79 |
80 | ///
81 | /// Converts a Type Tree into a Node Tree
82 | ///
83 | ///
84 | /// The root of the node tree
85 | private static UnityNode CreateRootNode(TypeTree tree)
86 | {
87 | if (tree == null)
88 | throw new ArgumentNullException(nameof(tree));
89 | UnityNode root = new UnityNode(tree[0]);
90 | UnityNode current = root;
91 | for (int i = 1; i < tree.Count; i++)
92 | {
93 | TypeTreeNode treeNode = tree[i];
94 |
95 | while (treeNode.Level <= current.Level)
96 | current = current.Parent;
97 |
98 | UnityNode newNode = new UnityNode(current, treeNode);
99 | current.SubNodes.Add(newNode);
100 | current = newNode;
101 | }
102 | return root;
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/TypeTreeDumper/UnityInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Unity;
3 |
4 | namespace TypeTreeDumper
5 | {
6 | internal class UnityInfo
7 | {
8 | public string Version { get; set; }
9 | public List Strings { get; set; }
10 | public List Classes { get; set; }
11 |
12 | public static UnityInfo Create(UnityEngine engine,
13 | TransferInstructionFlags releaseFlags = TransferInstructionFlags.SerializeGameRelease,
14 | TransferInstructionFlags editorFlags = TransferInstructionFlags.None)
15 | {
16 | var result = new UnityInfo();
17 | result.Version = engine.Version.ToString();
18 | result.Strings = UnityString.MakeList(engine.CommonString);
19 | result.Classes = UnityClass.MakeList(engine, releaseFlags, editorFlags);
20 | return result;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TypeTreeDumper/UnityNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Newtonsoft.Json;
3 | using Unity;
4 |
5 | namespace TypeTreeDumper
6 | {
7 | internal class UnityNode
8 | {
9 | public string TypeName { get; set; }
10 | public string Name { get; set; }
11 | public byte Level { get; set; }
12 | public int ByteSize { get; set; }
13 | public int Index { get; set; }
14 | public short Version { get; set; }
15 | public byte TypeFlags { get; set; }
16 | public uint MetaFlag { get; set; }
17 | public List SubNodes { get; set; }
18 | [JsonIgnore]
19 | public UnityNode Parent { get; set; }
20 |
21 | public UnityNode() { }
22 | public UnityNode(UnityNode parent, TypeTreeNode treeNode) : this(treeNode)
23 | {
24 | Parent = parent;
25 | }
26 | public UnityNode(TypeTreeNode treeNode)
27 | {
28 | Level = treeNode.Level;
29 | TypeName = treeNode.TypeName;
30 | Name = treeNode.Name;
31 | ByteSize = treeNode.ByteSize;
32 | Index = treeNode.Index;
33 | Version = treeNode.Version;
34 | TypeFlags = (byte)treeNode.TypeFlags;
35 | MetaFlag = (uint)treeNode.MetaFlag;
36 | SubNodes = new List();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/TypeTreeDumper/UnityString.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Text;
4 | using Unity;
5 |
6 | namespace TypeTreeDumper
7 | {
8 | internal class UnityString
9 | {
10 | public uint Index { get; set; }
11 | public string String { get; set; }
12 |
13 | internal unsafe static List MakeList(CommonString strings)
14 | {
15 | var data = strings.GetData();
16 | var result = new List();
17 |
18 | fixed (byte* pData = data)
19 | {
20 | using (var stream = new UnmanagedMemoryStream(pData, data.Length))
21 | {
22 | using (var reader = new BinaryReader(stream))
23 | {
24 | while (stream.Position < stream.Length)
25 | {
26 | uint position = (uint)stream.Position;
27 | string str = ReadStringToNull(reader);
28 | result.Add(new UnityString() { Index = position, String = str });
29 | }
30 | }
31 | }
32 | }
33 |
34 | return result;
35 | }
36 |
37 | private static string ReadStringToNull(BinaryReader reader, int maxLength = 32767)
38 | {
39 | var bytes = new List();
40 | int count = 0;
41 | while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
42 | {
43 | var b = reader.ReadByte();
44 | if (b == 0)
45 | {
46 | break;
47 | }
48 | bytes.Add(b);
49 | count++;
50 | }
51 | return Encoding.UTF8.GetString(bytes.ToArray());
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/UnityInterop/BasicString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Text;
4 |
5 | namespace Unity
6 | {
7 | unsafe struct BasicString
8 | {
9 | public StringRepresentationUnion m_data_union;
10 | public StringRepresentation m_data_repr;
11 | public MemLabelId m_label;
12 |
13 | ///
14 | /// Create a simple basic string
15 | ///
16 | ///
17 | public static BasicString CreateExternal()
18 | {
19 | return new BasicString
20 | {
21 | m_data_repr = StringRepresentation.External
22 | };
23 | }
24 |
25 | ///
26 | /// Create an external basic string
27 | ///
28 | /// var data = Marshal.StringToCoTaskMemUTF8(str);
29 | /// var length = (uint)new Span<byte>((void*)data, int.MaxValue).IndexOf((byte)0);
30 | ///
31 | /// try
32 | /// {
33 | /// var basicString = BasicString.CreateExternal((byte*)data, length);
34 | /// //Do interop
35 | /// }
36 | /// finally
37 | /// {
38 | /// Marshal.FreeCoTaskMem(data);
39 | /// }
40 | ///
41 | ///
42 | /// A pointer to some utf8 data. Can be null if the length is zero.
43 | /// The length of the utf8 data.
44 | /// A new basic string.
45 | public static BasicString CreateExternal(byte* address, nuint length)
46 | {
47 | return new BasicString
48 | {
49 | m_data_repr = StringRepresentation.External,
50 | m_data_union = new StringRepresentationUnion
51 | {
52 | m_heap = new HeapAllocatedRepresentation
53 | {
54 | m_data = address,
55 | m_capacity = length,
56 | m_size = length
57 | }
58 | }
59 | };
60 | }
61 |
62 | ///
63 | /// Create an embedded basic string from a C# managed string.
64 | ///
65 | ///
66 | /// If the string provided is larger than the space available, it will be truncated.
67 | ///
68 | /// The string to be copied.
69 | /// A new basic string containing the string.
70 | public static BasicString CreateEmbedded(string str) => CreateEmbedded(Encoding.UTF8.GetBytes(str));
71 |
72 | ///
73 | /// Create an embedded basic string from some utf8 data.
74 | ///
75 | ///
76 | /// If the data provided is larger than the space available, it will be truncated.
77 | ///
78 | /// The data to be copied.
79 | /// A new basic string containing the data.
80 | public static BasicString CreateEmbedded(ReadOnlySpan utf8String)
81 | {
82 | var basicString = new BasicString
83 | {
84 | m_data_repr = StringRepresentation.Embedded,
85 | };
86 |
87 | int size = Math.Min(sizeof(HeapAllocatedRepresentation), utf8String.Length);
88 | Span embeddedData = new Span(&basicString, sizeof(HeapAllocatedRepresentation));
89 | for(int i = 0; i < size; i++)
90 | {
91 | embeddedData[i] = utf8String[i];
92 | }
93 | basicString.m_data_union.m_embedded.m_extra = (byte)(sizeof(HeapAllocatedRepresentation) - size);
94 |
95 | return basicString;
96 | }
97 | }
98 |
99 | internal static class BasicStringExtensions
100 | {
101 | ///
102 | /// Get an equivalent managed string
103 | ///
104 | ///
105 | /// For data safety, this has to stay an extension method.
106 | ///
107 | /// The basic string to
108 | /// A new string made from the underlying utf8 data
109 | public unsafe static string GetString(this BasicString basicString)
110 | {
111 | ReadOnlySpan data = basicString.m_data_repr == StringRepresentation.Embedded
112 | ? new ReadOnlySpan(&basicString, sizeof(HeapAllocatedRepresentation) - basicString.m_data_union.m_embedded.m_extra)
113 | : new ReadOnlySpan(basicString.m_data_union.m_heap.m_data, (int)(uint)basicString.m_data_union.m_heap.m_capacity);
114 | //Note: capacity is used here instead of size because Unity doesn't set size on some versions (such as 2019.4.3)
115 |
116 | return Encoding.UTF8.GetString(data);
117 | }
118 | }
119 |
120 | unsafe struct HeapAllocatedRepresentation
121 | {
122 | public byte* m_data;
123 | public nuint m_capacity;
124 | public nuint m_size;
125 | }
126 |
127 | unsafe struct StackAllocatedRepresentation
128 | {
129 | // StackAllocatedRepresentation is sizeof(HeapAllocatedRepresentation) + sizeof(TChar)
130 | public HeapAllocatedRepresentation m_heap;
131 | public byte m_extra;
132 | }
133 |
134 | [StructLayout(LayoutKind.Explicit)]
135 | struct StringRepresentationUnion
136 | {
137 | [FieldOffset(0)]
138 | public StackAllocatedRepresentation m_embedded;
139 |
140 | [FieldOffset(0)]
141 | public HeapAllocatedRepresentation m_heap;
142 | }
143 |
144 | enum StringRepresentation : byte
145 | {
146 | ///
147 | /// The data is stored by Unity.
148 | ///
149 | Heap,
150 | ///
151 | /// The data is stored within the structure.
152 | ///
153 | Embedded,
154 | ///
155 | /// The data is stored outside of Unity.
156 | ///
157 | External
158 | };
159 | }
--------------------------------------------------------------------------------
/UnityInterop/CommonString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Unity
5 | {
6 | public unsafe class CommonString
7 | {
8 | public sbyte* BufferBegin { get; }
9 |
10 | public sbyte* BufferEnd { get; }
11 |
12 | public CommonString(SymbolResolver resolver)
13 | {
14 | if (resolver.TryResolve($"?BufferBegin@CommonString@Unity@@3Q{NameMangling.Ptr64}BD{NameMangling.Ptr64}B", out void* begin))
15 | BufferBegin = *(sbyte**)begin;
16 |
17 | if (resolver.TryResolve($"?BufferEnd@CommonString@Unity@@3Q{NameMangling.Ptr64}BD{NameMangling.Ptr64}B", out void* end))
18 | BufferEnd = *(sbyte**)end;
19 | }
20 |
21 | public unsafe ReadOnlySpan GetData()
22 | {
23 | if (BufferBegin == null || BufferEnd == null)
24 | return ReadOnlySpan.Empty;
25 |
26 | var source = (byte*)BufferBegin;
27 | var length = (byte*)BufferEnd - source - 1;
28 | return new ReadOnlySpan(source, (int)length);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/UnityInterop/DynamicArray.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace Unity
6 | {
7 | unsafe struct DynamicArray : IReadOnlyList
8 | where T : unmanaged
9 | {
10 | public T* Ptr;
11 | public TLabel Label;
12 | public ulong Size;
13 | public ulong Capacity;
14 |
15 | readonly T IReadOnlyList.this[int index] => Ptr[index];
16 |
17 | public readonly ref T this[int index] => ref Ptr[index];
18 |
19 | public readonly ref T this[ulong index] => ref Ptr[index];
20 |
21 | public readonly int Count => (int)Size;
22 |
23 | public readonly IEnumerator GetEnumerator()
24 | {
25 | for (ulong i = 0; i < Size; i++)
26 | yield return this[i];
27 | }
28 |
29 | readonly IEnumerator IEnumerable.GetEnumerator()
30 | {
31 | return GetEnumerator();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/UnityInterop/HideFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | [Flags]
6 | public enum HideFlags
7 | {
8 | None = 0,
9 | HideInHierarchy = 1 << 0,
10 | HideInInspector = 1 << 1,
11 | DontSaveInEditor = 1 << 2,
12 | NotEditable = 1 << 3,
13 | DontSaveInBuild = 1 << 4,
14 | DontUnloadUnusedAsset = 1 << 5,
15 | DontSave = 52,
16 | HideAndDontSave = 61
17 | }
18 | }
--------------------------------------------------------------------------------
/UnityInterop/JsonHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | public unsafe class JsonHandler
6 | {
7 | public bool IsSupported { get; } = true;
8 | private static string SerializeObjectSymbolName => $"?SerializeObject@JSONUtility@@SAXP{NameMangling.Ptr64}AVObject@@AEAV?$basic_string@DV?$StringStorageDefault@D@core@@@core@@_NW4TransferInstructionFlags@@@Z";
9 | private readonly delegate* unmanaged[Cdecl] s_SerializeObject;
10 |
11 | public JsonHandler(UnityVersion version, SymbolResolver resolver)
12 | {
13 | if(resolver.TryResolve(SerializeObjectSymbolName, out var address))
14 | {
15 | s_SerializeObject = (delegate* unmanaged[Cdecl])address;
16 | }
17 | else
18 | {
19 | IsSupported = false;
20 | }
21 | }
22 |
23 | ///
24 | ///
25 | ///
26 | ///
27 | /// Flags for the method call. They don't seem to matter.
28 | ///
29 | ///
30 | ///
31 | public unsafe string SerializeObjectAsJson(NativeObject @object, TransferInstructionFlags flags = TransferInstructionFlags.None, bool prettyPrint = true)
32 | {
33 | if (s_SerializeObject != null)
34 | {
35 | BasicString basicString = BasicString.CreateExternal();
36 | s_SerializeObject(@object.Pointer, &basicString, prettyPrint ? (byte)1 : (byte)0, flags);
37 | return basicString.GetString();
38 | }
39 | else
40 | {
41 | throw new NotSupportedException();
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/UnityInterop/MemLabelId.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | struct MemLabelId
4 | {
5 | public AllocationRootWithSalt RootReference;
6 | public MemoryLabelIdentifier Identifier;
7 |
8 | public override string ToString()
9 | {
10 | return $"MemLabelId Salt: 0x{RootReference.Salt:X} RootReferenceIndex: 0x{RootReference.RootReferenceIndex:X} Identifier: {Identifier}";
11 | }
12 |
13 | ///
14 | /// 2020.2.6 and higher
15 | ///
16 | public static MemLabelId DefaultMemBaseObject_2020_2_6 { get; } = new()
17 | {
18 | RootReference = AllocationRootWithSalt.Default,
19 | Identifier = (MemoryLabelIdentifier)56,
20 | };
21 |
22 | ///
23 | /// 2020.2.6 and higher
24 | ///
25 | public static MemLabelId DefaultMemTypeTree_2020_2_6 { get; } = new()
26 | {
27 | RootReference = AllocationRootWithSalt.Default,
28 | Identifier = (MemoryLabelIdentifier)83,
29 | };
30 | }
31 |
32 | struct AllocationRootWithSalt
33 | {
34 | public uint Salt;
35 | public uint RootReferenceIndex;
36 |
37 | public static AllocationRootWithSalt Default { get; } = new()
38 | {
39 | Salt = 0,
40 | RootReferenceIndex = uint.MaxValue
41 | };
42 | }
43 |
44 | ///
45 | /// Content changes often between Unity versions
46 | ///
47 | enum MemoryLabelIdentifier
48 | {
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/UnityInterop/NameMangling.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | public static class NameMangling
6 | {
7 | public static string Ptr64
8 | {
9 | get => Environment.Is64BitProcess ? "E" : "";
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/UnityInterop/NativeObject/NativeObject.V1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Specialized;
3 |
4 | namespace Unity
5 | {
6 | public partial class NativeObject
7 | {
8 | internal unsafe class V1 : INativeObjectImpl
9 | {
10 | static readonly BitVector32.Section MemLabelIdentifierSection = BitVector32.CreateSection(1 << 12);
11 |
12 | static readonly BitVector32.Section IsRootOwnerSection = BitVector32.CreateSection(1 << 0, MemLabelIdentifierSection);
13 |
14 | static readonly BitVector32.Section TemporaryFlagsSection = BitVector32.CreateSection(1 << 0, IsRootOwnerSection);
15 |
16 | static readonly BitVector32.Section HideFlagsSection = BitVector32.CreateSection(1 << 3, TemporaryFlagsSection);
17 |
18 | static readonly BitVector32.Section IsPersistentSection = BitVector32.CreateSection(1 << 0, HideFlagsSection);
19 |
20 | static readonly BitVector32.Section CachedTypeIndexSection = BitVector32.CreateSection(1 << 11, IsPersistentSection);
21 |
22 | NativeObject* nativeObject;
23 |
24 | public int InstanceID => nativeObject->InstanceID;
25 |
26 | public void* Pointer => nativeObject;
27 |
28 | public byte TemporaryFlags
29 | {
30 | get { return (byte)nativeObject->bits[TemporaryFlagsSection]; }
31 | }
32 |
33 | public HideFlags HideFlags
34 | {
35 | get { return (HideFlags)nativeObject->bits[HideFlagsSection]; }
36 | }
37 |
38 | public bool IsPersistent
39 | {
40 | get { return nativeObject->bits[IsPersistentSection] != 0; }
41 | }
42 |
43 | public uint CachedTypeIndex
44 | {
45 | get { return (uint)nativeObject->bits[CachedTypeIndexSection]; }
46 | }
47 |
48 | public V1(void* ptr)
49 | {
50 | nativeObject = (NativeObject*)ptr;
51 | }
52 |
53 | internal struct NativeObject
54 | {
55 | public IntPtr* VirtualFunctionTable;
56 | public int InstanceID;
57 | public BitVector32 bits;
58 | // There are more fields but they aren't needed.
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/UnityInterop/NativeObject/NativeObject.V5_0.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Specialized;
3 |
4 | namespace Unity
5 | {
6 | public partial class NativeObject
7 | {
8 | // Unity 5.0+
9 | internal unsafe class V5_0 : INativeObjectImpl
10 | {
11 | static readonly BitVector32.Section MemLabelIdentifierSection = BitVector32.CreateSection(1 << 11);
12 |
13 | static readonly BitVector32.Section TemporaryFlagsSection = BitVector32.CreateSection(1 << 0, MemLabelIdentifierSection);
14 |
15 | static readonly BitVector32.Section HideFlagsSection = BitVector32.CreateSection(1 << 6, TemporaryFlagsSection);
16 |
17 | static readonly BitVector32.Section IsPersistentSection = BitVector32.CreateSection(1 << 0, HideFlagsSection);
18 |
19 | static readonly BitVector32.Section CachedTypeIndexSection = BitVector32.CreateSection(1 << 10, IsPersistentSection);
20 |
21 | NativeObject* nativeObject;
22 |
23 | public int InstanceID => nativeObject->InstanceID;
24 |
25 | public void* Pointer => nativeObject;
26 |
27 | public byte TemporaryFlags
28 | {
29 | get { return (byte)nativeObject->bits[TemporaryFlagsSection]; }
30 | }
31 |
32 | public HideFlags HideFlags
33 | {
34 | get { return (HideFlags)nativeObject->bits[HideFlagsSection]; }
35 | }
36 |
37 | public bool IsPersistent
38 | {
39 | get { return nativeObject->bits[IsPersistentSection] != 0; }
40 | }
41 |
42 | public uint CachedTypeIndex
43 | {
44 | get { return (uint)nativeObject->bits[CachedTypeIndexSection]; }
45 | }
46 |
47 | public V5_0(void* ptr)
48 | {
49 | nativeObject = (NativeObject*)ptr;
50 | }
51 |
52 | internal struct NativeObject
53 | {
54 | public IntPtr* VirtualFunctionTable;
55 | public int InstanceID;
56 | public BitVector32 bits;
57 | // There are more fields but they aren't needed.
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/UnityInterop/NativeObject/NativeObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | public unsafe partial class NativeObject
6 | {
7 | INativeObjectImpl nativeObject;
8 |
9 | NativeObjectFactory factory;
10 |
11 | PersistentTypeID persistentTypeID;
12 |
13 | public int InstanceID => nativeObject.InstanceID;
14 |
15 | public void* Pointer => nativeObject.Pointer;
16 |
17 | public NativeObject(void* ptr, NativeObjectFactory factory, PersistentTypeID persistentTypeID, UnityVersion version)
18 | {
19 | if (ptr == null)
20 | throw new ArgumentNullException(nameof(ptr));
21 |
22 | if (version < UnityVersion.Unity5_0)
23 | {
24 | nativeObject = new V1(ptr);
25 | }
26 | else
27 | {
28 | nativeObject = new V5_0(ptr);
29 | }
30 | this.factory = factory;
31 | this.persistentTypeID = persistentTypeID;
32 | }
33 |
34 | public byte TemporaryFlags => nativeObject.TemporaryFlags;
35 |
36 | public HideFlags HideFlags => nativeObject.HideFlags;
37 |
38 | public bool IsPersistent => nativeObject.IsPersistent;
39 |
40 | public uint CachedTypeIndex => nativeObject.CachedTypeIndex;
41 |
42 | interface INativeObjectImpl
43 | {
44 | int InstanceID { get; }
45 | void* Pointer { get; }
46 | public byte TemporaryFlags { get; }
47 | public HideFlags HideFlags { get; }
48 | public bool IsPersistent { get; }
49 | public uint CachedTypeIndex { get; }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/UnityInterop/NativeObject/NativeObjectFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | public unsafe class NativeObjectFactory
4 | {
5 | readonly UnityVersion version;
6 | readonly SymbolResolver resolver;
7 |
8 | readonly MemLabelId* kMemBaseObject;
9 |
10 | readonly delegate* unmanaged[Cdecl] s_GetSpriteAtlasDatabase;
11 |
12 | readonly delegate* unmanaged[Cdecl] s_GetSceneVisibilityState;
13 |
14 | readonly delegate* unmanaged[Cdecl] s_GetInspectorExpandedState;
15 |
16 | readonly delegate* unmanaged[Cdecl] s_GetAnnotationManager;
17 |
18 | readonly delegate* unmanaged[Cdecl] s_GetMonoManager;
19 |
20 | readonly delegate* unmanaged[Cdecl] s_GetTimeManager;
21 |
22 | readonly delegate* unmanaged[Cdecl] s_ProduceV3_4;
23 |
24 | readonly delegate* unmanaged[Cdecl] s_ProduceV3_5;
25 |
26 | readonly delegate* unmanaged[Cdecl] s_ProduceV5_5;
27 |
28 | readonly delegate* unmanaged[Cdecl] s_ProduceV2017_2;
29 |
30 | readonly delegate* unmanaged[Cdecl] s_ProduceV2023_1_0a2;
31 |
32 | bool HasGetSceneVisibilityState => version >= UnityVersion.Unity2019_1;
33 |
34 | bool HasGetSpriteAtlasDatabase => version >= UnityVersion.Unity2017_1;
35 |
36 | bool HasGetTimeManager => false;
37 |
38 | public NativeObjectFactory(UnityVersion version, SymbolResolver resolver)
39 | {
40 | this.version = version;
41 | this.resolver = resolver;
42 |
43 | if (HasGetTimeManager)
44 | s_GetTimeManager = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetTimeManager@@YAA{NameMangling.Ptr64}AVTimeManager@@XZ");
45 |
46 | if (HasGetSpriteAtlasDatabase)
47 | s_GetSpriteAtlasDatabase = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetSpriteAtlasDatabase@@YAA{NameMangling.Ptr64}AVSpriteAtlasDatabase@@XZ");
48 |
49 | if (HasGetSceneVisibilityState)
50 | s_GetSceneVisibilityState = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetSceneVisibilityState@@YAA{NameMangling.Ptr64}AVSceneVisibilityState@@XZ");
51 |
52 | s_GetInspectorExpandedState = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetInspectorExpandedState@@YAA{NameMangling.Ptr64}AVInspectorExpandedState@@XZ");
53 | s_GetInspectorExpandedState = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetInspectorExpandedState@@YAA{NameMangling.Ptr64}AVInspectorExpandedState@@XZ");
54 | s_GetAnnotationManager = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetAnnotationManager@@YAA{NameMangling.Ptr64}AVAnnotationManager@@XZ");
55 | s_GetMonoManager = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetMonoManager@@YAA{NameMangling.Ptr64}AVMonoManager@@XZ");
56 |
57 | if (version < UnityVersion.Unity3_5)
58 | s_ProduceV3_4 = (delegate* unmanaged[Cdecl])resolver.Resolve($"?Produce@Object@@SAP{NameMangling.Ptr64}AV1@HHP{NameMangling.Ptr64}AVBaseAllocator@@W4ObjectCreationMode@@@Z");
59 | else if (version < UnityVersion.Unity5_5)
60 | s_ProduceV3_5 = (delegate* unmanaged[Cdecl])resolver.Resolve($"?Produce@Object@@SAP{NameMangling.Ptr64}AV1@HHUMemLabelId@@W4ObjectCreationMode@@@Z");
61 | else if (version < UnityVersion.Unity2017_2)
62 | s_ProduceV5_5 = (delegate* unmanaged[Cdecl])resolver.Resolve($"?Produce@Object@@SAP{NameMangling.Ptr64}AV1@P{NameMangling.Ptr64}BVType@Unity@@HUMemLabelId@@W4ObjectCreationMode@@@Z");
63 | else
64 | {
65 | // https://github.com/DaZombieKiller/TypeTreeDumper/issues/42
66 | var symbolName = version < new UnityVersion(6000, 2, 0, UnityVersionType.Alpha, 8)
67 | ? $"?Produce@Object@@CAP{NameMangling.Ptr64}AV1@P{NameMangling.Ptr64}BVType@Unity@@0HUMemLabelId@@W4ObjectCreationMode@@@Z"
68 | : $"?Produce@Object@@CAP{NameMangling.Ptr64}AV1@P{NameMangling.Ptr64}BVType@Unity@@0UEntityId@@UMemLabelId@@W4ObjectCreationMode@@@Z";
69 | s_ProduceV2017_2 = (delegate* unmanaged[Cdecl])resolver.Resolve(symbolName);
70 | s_ProduceV2023_1_0a2 = (delegate* unmanaged[Cdecl])s_ProduceV2017_2;
71 | }
72 |
73 | if (version >= UnityVersion.Unity3_5 && version < UnityVersion.Unity2022_2)
74 | {
75 | kMemBaseObject = resolver.Resolve(
76 | "?kMemBaseObject@@3UMemLabelId@@A",
77 | "?kMemBaseObject@@3UkMemBaseObjectStruct@@A"
78 | );
79 | }
80 | }
81 |
82 | public NativeObject GetSpriteAtlasDatabase()
83 | {
84 | return new NativeObject(s_GetSpriteAtlasDatabase(), this, PersistentTypeID.SpriteAtlasDatabase, version);
85 | }
86 |
87 | public NativeObject GetSceneVisibilityState()
88 | {
89 | return new NativeObject(s_GetSceneVisibilityState(), this, PersistentTypeID.SceneVisibilityState, version);
90 | }
91 |
92 | public NativeObject GetInspectorExpandedState()
93 | {
94 | return new NativeObject(s_GetInspectorExpandedState(), this, PersistentTypeID.InspectorExpandedState, version);
95 | }
96 |
97 | public NativeObject GetAnnotationManager()
98 | {
99 | return new NativeObject(s_GetAnnotationManager(), this, PersistentTypeID.AnnotationManager, version);
100 | }
101 |
102 | public NativeObject GetMonoManager()
103 | {
104 | return new NativeObject(s_GetMonoManager(), this, PersistentTypeID.MonoManager, version);
105 | }
106 |
107 | public NativeObject GetTimeManager()
108 | {
109 | return new NativeObject(s_GetTimeManager(), this, PersistentTypeID.TimeManager, version);
110 | }
111 |
112 | public NativeObject Produce(RuntimeTypeInfo type, int instanceID, ObjectCreationMode creationMode)
113 | {
114 | // TODO: Support producing abstract types. To do this, the following steps are necessary:
115 | // 1. Replace T::VirtualRedirectTransfer with T::Transfer. This can be done by either
116 | // hooking the method via EasyHook, or modifying the virtual function table.
117 | // This works because both methods have compatible signatures.
118 | // 2. Create a new Factory method for the type, by locating its constructor function
119 | // and using that to create a new delegate.
120 | // 3. Create a new RuntimeTypeInfo based on the original, with the new Factory method.
121 | // It also needs to have the IsAbstract field set to false.
122 | // 4. Hook T::GetTypeVirtualInternal to return the appropriate RuntimeTypeInfo.
123 | if (type.IsAbstract)
124 | return null;
125 |
126 | void* ptr;
127 | if (version < UnityVersion.Unity3_5)
128 | {
129 | ptr = s_ProduceV3_4((int)type.PersistentTypeID, instanceID, null, creationMode);
130 | }
131 | else if (version < UnityVersion.Unity5_5)
132 | {
133 | ptr = s_ProduceV3_5((int)type.PersistentTypeID, instanceID, *kMemBaseObject, creationMode);
134 | }
135 | else if (version < UnityVersion.Unity2017_2)
136 | {
137 | fixed (byte* typePtr = &type.GetPinnableReference())
138 | ptr = s_ProduceV5_5(typePtr, instanceID, *kMemBaseObject, creationMode);
139 | }
140 | else
141 | {
142 | // TODO: Why does this take two types?
143 | // The first type parameter is the source type.
144 | // The second type parameter is the destination type. If default, this function returns null.
145 | fixed (byte* typePtr = &type.GetPinnableReference())
146 | {
147 | if (version < UnityVersion.Unity2023_1_0a2)
148 | {
149 | MemLabelId labelId = kMemBaseObject != null ? *kMemBaseObject : MemLabelId.DefaultMemBaseObject_2020_2_6;
150 | ptr = s_ProduceV2017_2(typePtr, typePtr, instanceID, labelId, creationMode);
151 | }
152 | else
153 | {
154 | MemoryLabelIdentifier labelId = kMemBaseObject != null ? *(MemoryLabelIdentifier*)kMemBaseObject : MemLabelId.DefaultMemBaseObject_2020_2_6.Identifier;
155 | ptr = s_ProduceV2023_1_0a2(typePtr, typePtr, instanceID, labelId, creationMode);
156 | }
157 | }
158 | }
159 |
160 | return ptr == null ? null : new NativeObject(ptr, this, type.PersistentTypeID, version);
161 | }
162 |
163 | public NativeObject GetOrProduce(RuntimeTypeInfo type) => type.PersistentTypeID switch
164 | {
165 | PersistentTypeID.SpriteAtlasDatabase => GetSpriteAtlasDatabase(),
166 | PersistentTypeID.SceneVisibilityState => GetSceneVisibilityState(),
167 | PersistentTypeID.InspectorExpandedState => GetInspectorExpandedState(),
168 | PersistentTypeID.AnnotationManager => GetAnnotationManager(),
169 | PersistentTypeID.MonoManager => GetMonoManager(),
170 | //PersistentTypeID.TimeManager => GetTimeManager(),
171 | _ => Produce(type, 0, ObjectCreationMode.Default),
172 | };
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/UnityInterop/ObjectCreationMode.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | public enum ObjectCreationMode
4 | {
5 | Default = 0,
6 | FromNonMainThread = 1,
7 | DefaultNoLock = 2,
8 | }
9 | }
--------------------------------------------------------------------------------
/UnityInterop/PersistentTypeID.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | public enum PersistentTypeID
4 | {
5 | Object = 0,
6 | AnnotationManager = 1049,
7 | AssetDatabaseV1 = 1004,
8 | AssetMetaData = 1028,
9 | AudioBuildInfo = 641289076,
10 | BuiltAssetBundleInfoSet = 668709126,
11 | Derived = 1091556383,
12 | SubDerived = 367388927,
13 | EditorBuildSettings = 1045,
14 | EditorExtension = 18,
15 | Component = 2,
16 | Behaviour = 8,
17 | Animation = 111,
18 | Animator = 95,
19 | ArticulationBody = 171741748,
20 | AudioBehaviour = 180,
21 | AudioListener = 81,
22 | AudioSource = 82,
23 | AudioFilter = 181,
24 | AudioChorusFilter = 166,
25 | AudioDistortionFilter = 170,
26 | AudioEchoFilter = 168,
27 | AudioHighPassFilter = 165,
28 | AudioLowPassFilter = 169,
29 | AudioReverbFilter = 164,
30 | AudioReverbZone = 167,
31 | Camera = 20,
32 | Canvas = 223,
33 | CanvasGroup = 225,
34 | Cloth = 183,
35 | Collider2D = 53,
36 | BoxCollider2D = 61,
37 | CapsuleCollider2D = 70,
38 | CircleCollider2D = 58,
39 | CompositeCollider2D = 66,
40 | EdgeCollider2D = 68,
41 | PolygonCollider2D = 60,
42 | TilemapCollider2D = 19719996,
43 | ConstantForce = 75,
44 | Effector2D = 248,
45 | AreaEffector2D = 249,
46 | BuoyancyEffector2D = 253,
47 | PlatformEffector2D = 251,
48 | PointEffector2D = 250,
49 | SurfaceEffector2D = 252,
50 | FlareLayer = 124,
51 | GridLayout = 1742807556,
52 | Grid = 156049354,
53 | Tilemap = 1839735485,
54 | Halo = 122,
55 | HaloLayer = 125,
56 | IConstraint = 285090594,
57 | AimConstraint = 895512359,
58 | LookAtConstraint = 1183024399,
59 | ParentConstraint = 1773428102,
60 | PositionConstraint = 1818360608,
61 | RotationConstraint = 1818360609,
62 | ScaleConstraint = 1818360610,
63 | Joint2D = 230,
64 | AnchoredJoint2D = 229,
65 | DistanceJoint2D = 232,
66 | FixedJoint2D = 255,
67 | FrictionJoint2D = 256,
68 | HingeJoint2D = 233,
69 | SliderJoint2D = 234,
70 | SpringJoint2D = 231,
71 | WheelJoint2D = 235,
72 | RelativeJoint2D = 254,
73 | TargetJoint2D = 257,
74 | LensFlare = 123,
75 | Light = 108,
76 | LightProbeGroup = 220,
77 | LightProbeProxyVolume = 259,
78 | MonoBehaviour = 114,
79 | NavMeshAgent = 195,
80 | NavMeshObstacle = 208,
81 | OffMeshLink = 191,
82 | ParticleSystemForceField = 330,
83 | PhysicsUpdateBehaviour2D = 246,
84 | ConstantForce2D = 247,
85 | PlayableDirector = 320,
86 | Projector = 119,
87 | ReflectionProbe = 215,
88 | Skybox = 45,
89 | SortingGroup = 210,
90 | StreamingController = 1542919678,
91 | Terrain = 218,
92 | VideoPlayer = 328,
93 | VisualEffect = 2083052967,
94 | WindZone = 182,
95 | CanvasRenderer = 222,
96 | Collider = 56,
97 | BoxCollider = 65,
98 | CapsuleCollider = 136,
99 | CharacterController = 143,
100 | MeshCollider = 64,
101 | SphereCollider = 135,
102 | TerrainCollider = 154,
103 | WheelCollider = 146,
104 | FakeComponent = 1803986026,
105 | Joint = 57,
106 | CharacterJoint = 144,
107 | ConfigurableJoint = 153,
108 | FixedJoint = 138,
109 | HingeJoint = 59,
110 | SpringJoint = 145,
111 | LODGroup = 205,
112 | MeshFilter = 33,
113 | OcclusionArea = 192,
114 | OcclusionPortal = 41,
115 | ParticleSystem = 198,
116 | Renderer = 25,
117 | BillboardRenderer = 227,
118 | LineRenderer = 120,
119 | RendererFake = 646504946,
120 | MeshRenderer = 23,
121 | ParticleSystemRenderer = 199,
122 | SkinnedMeshRenderer = 137,
123 | SpriteMask = 331,
124 | SpriteRenderer = 212,
125 | SpriteShapeRenderer = 1971053207,
126 | TilemapRenderer = 483693784,
127 | TrailRenderer = 96,
128 | VFXRenderer = 73398921,
129 | Rigidbody = 54,
130 | Rigidbody2D = 50,
131 | TextMesh = 102,
132 | Transform = 4,
133 | RectTransform = 224,
134 | Tree = 193,
135 | WorldAnchor = 362,
136 | GameObject = 1,
137 | NamedObject = 130,
138 | AnimatorState = 1102,
139 | AnimatorStateMachine = 1107,
140 | AnimatorTransitionBase = 1111,
141 | AnimatorStateTransition = 1101,
142 | AnimatorTransition = 1109,
143 | AssetBundle = 142,
144 | AssetBundleManifest = 290,
145 | AssetImportInProgressProxy = 369655926,
146 | AssetImporter = 1003,
147 | AssemblyDefinitionImporter = 1766753193,
148 | AssemblyDefinitionReferenceImporter = 294290339,
149 | AudioImporter = 1020,
150 | ComputeShaderImporter = 1008,
151 | DefaultImporter = 1030,
152 | IHVImageFormatImporter = 1055,
153 | LibraryAssetImporter = 1038,
154 | LocalizationImporter = 1027052791,
155 | ModelImporter = 1040,
156 | FBXImporter = 1041,
157 | Mesh3DSImporter = 1005,
158 | SketchUpImporter = 1124,
159 | MonoImporter = 1035,
160 | MultiArtifactTestImporter = 1223240404,
161 | NativeFormatImporter = 1034,
162 | PackageManifestImporter = 1896753126,
163 | PluginImporter = 1050,
164 | PrefabImporter = 468431735,
165 | RayTracingShaderImporter = 747330370,
166 | ReferencesArtifactGenerator = 1114811875,
167 | ScriptedImporter = 2089858483,
168 | ShaderImporter = 1007,
169 | SpeedTreeImporter = 1110,
170 | SpriteAtlasImporter = 1210832254,
171 | SubstanceImporter = 1112,
172 | TextScriptImporter = 1031,
173 | TextureImporter = 1006,
174 | TrueTypeFontImporter = 1042,
175 | VideoClipImporter = 1127,
176 | VisualEffectImporter = 2058629510,
177 | AssetImporterLog = 41386430,
178 | AudioMixer = 240,
179 | AudioMixerController = 241,
180 | AudioMixerEffectController = 244,
181 | AudioMixerGroup = 273,
182 | AudioMixerGroupController = 243,
183 | AudioMixerSnapshot = 272,
184 | AudioMixerSnapshotController = 245,
185 | Avatar = 90,
186 | AvatarMask = 319,
187 | BaseAnimationTrack = 110,
188 | NewAnimationTrack = 118,
189 | BillboardAsset = 226,
190 | BuildReport = 1125,
191 | CachedSpriteAtlas = 214,
192 | CachedSpriteAtlasRuntimeData = 644342135,
193 | ComputeShader = 72,
194 | DefaultAsset = 1029,
195 | SceneAsset = 1032,
196 | EditorProjectAccess = 426301858,
197 | Flare = 121,
198 | Font = 128,
199 | GameObjectRecorder = 1268269756,
200 | HumanTemplate = 1105,
201 | LightProbes = 258,
202 | LightingDataAsset = 1120,
203 | LightingDataAssetParent = 1325145578,
204 | LightingSettings = 850595691,
205 | LightmapParameters = 1113,
206 | LocalizationAsset = 2083778819,
207 | Material = 21,
208 | ProceduralMaterial = 185,
209 | Mesh = 43,
210 | Motion = 207,
211 | AnimationClip = 74,
212 | PreviewAnimationClip = 1108,
213 | BlendTree = 206,
214 | NavMeshData = 238,
215 | OcclusionCullingData = 363,
216 | PhysicMaterial = 134,
217 | PhysicsMaterial2D = 62,
218 | PreloadData = 150,
219 | Preset = 181963792,
220 | RayTracingShader = 825902497,
221 | RuntimeAnimatorController = 93,
222 | AnimatorController = 91,
223 | AnimatorOverrideController = 221,
224 | SampleClip = 271,
225 | AudioClip = 83,
226 | Shader = 48,
227 | ShaderVariantCollection = 200,
228 | SpeedTreeWindAsset = 228,
229 | Sprite = 213,
230 | SpriteAtlas = 687078895,
231 | SpriteAtlasAsset = 612988286,
232 | SubstanceArchive = 184,
233 | TerrainData = 156,
234 | TerrainLayer = 1953259897,
235 | TextAsset = 49,
236 | AssemblyDefinitionAsset = 1152215463,
237 | AssemblyDefinitionReferenceAsset = 662584278,
238 | CGProgram = 109,
239 | MonoScript = 115,
240 | PackageManifest = 1896753125,
241 | Texture = 27,
242 | BaseVideoTexture = 237,
243 | WebCamTexture = 158,
244 | CubemapArray = 188,
245 | LowerResBlitTexture = 1480428607,
246 | MovieTexture = 152,
247 | ProceduralTexture = 186,
248 | RenderTexture = 84,
249 | CustomRenderTexture = 86,
250 | SparseTexture = 171,
251 | Texture2D = 28,
252 | Cubemap = 89,
253 | Texture2DArray = 187,
254 | Texture3D = 117,
255 | VideoClip = 329,
256 | VisualEffectObject = 2059678085,
257 | VisualEffectAsset = 2058629509,
258 | VisualEffectSubgraph = 994735392,
259 | VisualEffectSubgraphBlock = 994735404,
260 | VisualEffectSubgraphOperator = 994735403,
261 | VisualEffectResource = 2058629511,
262 | EditorExtensionImpl = 1002,
263 | EditorSettings = 159,
264 | EditorUserBuildSettings = 1051,
265 | EditorUserSettings = 162,
266 | EmptyObject = 277625683,
267 | GameManager = 9,
268 | GlobalGameManager = 6,
269 | AudioManager = 11,
270 | BuildSettings = 141,
271 | ClusterInputManager = 236,
272 | DelayedCallManager = 98,
273 | GraphicsSettings = 30,
274 | InputManager = 13,
275 | MonoManager = 116,
276 | NavMeshProjectSettings = 126,
277 | Physics2DSettings = 19,
278 | PhysicsManager = 55,
279 | PlayerSettings = 129,
280 | QualitySettings = 47,
281 | ResourceManager = 147,
282 | RuntimeInitializeOnLoadManager = 300,
283 | ScriptMapper = 94,
284 | StreamingManager = 1403656975,
285 | TagManager = 78,
286 | TimeManager = 5,
287 | UnityConnectSettings = 310,
288 | VFXManager = 937362698,
289 | LevelGameManager = 3,
290 | LightmapSettings = 157,
291 | NavMeshSettings = 196,
292 | OcclusionCullingSettings = 29,
293 | RenderSettings = 104,
294 | HierarchyState = 1026,
295 | InspectorExpandedState = 1048,
296 | NativeObjectType = 1977754360,
297 | PackedAssets = 1126,
298 | PlatformModuleSetup = 877146078,
299 | PluginBuildInfo = 382020655,
300 | Prefab = 1001480554,
301 | PrefabInstance = 1001,
302 | PresetManager = 1386491679,
303 | PropertyModificationsTargetTestObject = 1111377672,
304 | SceneVisibilityState = 1154873562,
305 | ScenesUsingAssets = 156483287,
306 | SerializableManagedHost = 1995898324,
307 | SerializableManagedRefTestClass = 76251197,
308 | SiblingDerived = 334799969,
309 | SpriteAtlasDatabase = 638013454,
310 | TestObjectVectorPairStringBool = 1628831178,
311 | TestObjectWithSerializedAnimationCurve = 478637459,
312 | TestObjectWithSerializedArray = 478637458,
313 | TestObjectWithSerializedMapStringBool = 1981279845,
314 | TestObjectWithSerializedMapStringNonAlignedStruct = 342846651,
315 | TestObjectWithSpecialLayoutOne = 293259124,
316 | TestObjectWithSpecialLayoutTwo = 1392443030,
317 | VersionControlSettings = 890905787,
318 | Vector3f = 100005,
319 | AudioMixerLiveUpdateBool = 100009,
320 | @bool = 100001,
321 | @void = 100011,
322 | RootMotionData = 100006,
323 | AudioMixerLiveUpdateFloat = 100008,
324 | MonoObject = 100003,
325 | Collision2D = 100007,
326 | Polygon2D = 100010,
327 | Collision = 100004,
328 | @float = 100002,
329 | @int = 100000,
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimePlatform.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | public enum RuntimePlatform
4 | {
5 | WindowsEditor = 7
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeArray.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 |
7 | namespace Unity
8 | {
9 | public class RuntimeTypeArray : IReadOnlyList
10 | {
11 | readonly List types;
12 |
13 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
14 | unsafe delegate NativeTypeArray* GetRuntimeTypesDelegate();
15 |
16 | public RuntimeTypeInfo this[int index] => types[index];
17 |
18 | //The number of runtime types varies from version to version, we need a limit that is at least as
19 | //large as the largest RuntimeTypeId from version 3.4 to 5.5 so that we don't skip over any RuntimeTypes.
20 | //The largest seen is version 5.4.6f3 is 1126, but MaxRuntimeTypeId is set to 2000 just in case.
21 | const int MaxRuntimeTypeId = 2000;
22 |
23 | public int Count => types.Count;
24 |
25 | unsafe public RuntimeTypeArray(UnityVersion version, SymbolResolver resolver)
26 | {
27 | NativeTypeArray* runtimeTypes;
28 | if (version >= UnityVersion.Unity5_5)
29 | {
30 | if (version >= UnityVersion.Unity2017_3)
31 | {
32 | runtimeTypes = resolver.Resolve("?ms_runtimeTypes@RTTI@@0URuntimeTypeArray@1@A");
33 | }
34 | else
35 | {
36 | runtimeTypes = resolver.Resolve("?ms_runtimeTypes@RTTI@@2URuntimeTypeArray@1@A");
37 | }
38 |
39 | types = new List(runtimeTypes->Count);
40 | for (int i = 0; i < runtimeTypes->Count; i++)
41 | {
42 | var info = (&runtimeTypes->First)[i];
43 | types.Add(new RuntimeTypeInfo(info, resolver, version));
44 | }
45 | }
46 | else
47 | {
48 | delegate* unmanaged[Cdecl] ClassIDToRTTI;
49 |
50 | if (version >= UnityVersion.Unity5_4)
51 | *(void**)&ClassIDToRTTI = resolver.Resolve($"?ClassIDToRTTI@Object@@SAP{NameMangling.Ptr64}AURTTI@@H@Z");
52 | else if (version >= UnityVersion.Unity5_0)
53 | *(void**)&ClassIDToRTTI = resolver.Resolve($"?ClassIDToRTTI@Object@@SAP{NameMangling.Ptr64}AURTTI@1@H@Z");
54 | else
55 | *(void**)&ClassIDToRTTI = resolver.Resolve($"?ClassIDToRTTI@Object@@SAP{NameMangling.Ptr64}AURTTI@1@H@Z");
56 |
57 | types = new List();
58 | for(int i = 0; i < MaxRuntimeTypeId; i++)
59 | {
60 | var info = ClassIDToRTTI(i);
61 | if(info != null)
62 | {
63 | types.Add(new RuntimeTypeInfo((IntPtr)info, resolver, version));
64 | }
65 | }
66 | }
67 | foreach(RuntimeTypeInfo type in types)
68 | {
69 | if(type.Base != null)
70 | {
71 | var baseType = GetFromName(type.Base.FullName);
72 | baseType.Derived.Add(type);
73 | }
74 | }
75 | }
76 |
77 | public IEnumerator GetEnumerator()
78 | {
79 | return types.GetEnumerator();
80 | }
81 |
82 | IEnumerator IEnumerable.GetEnumerator()
83 | {
84 | return types.GetEnumerator();
85 | }
86 |
87 | unsafe struct NativeTypeArray
88 | {
89 | public int Count;
90 | public IntPtr First;
91 | }
92 |
93 | private RuntimeTypeInfo GetFromName(string fullName) => types.Where(x => x.FullName == fullName).FirstOrDefault();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V2017_3.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
5 |
6 | namespace Unity
7 | {
8 | public partial class RuntimeTypeInfo
9 | {
10 | // Unity 2017.3+
11 | unsafe class V2017_3 : IRuntimeTypeInfoImpl
12 | {
13 | RuntimeTypeInfo* TypeInfo;
14 |
15 | public ManagedRuntimeTypeInfo Base { get; }
16 |
17 | public string Name { get; }
18 |
19 | public string Namespace { get; }
20 |
21 | public string Module { get; }
22 |
23 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
24 |
25 | public int Size => TypeInfo->Size;
26 |
27 | public uint TypeIndex => TypeInfo->DerivedFromInfo.TypeIndex;
28 |
29 | public uint DescendantCount => TypeInfo->DerivedFromInfo.DescendantCount;
30 |
31 | public bool IsAbstract => TypeInfo->IsAbstract;
32 |
33 | public bool IsSealed => TypeInfo->IsSealed;
34 |
35 | public bool IsEditorOnly => TypeInfo->IsEditorOnly;
36 |
37 | public bool IsStripped => TypeInfo->IsStripped;
38 |
39 | public IntPtr Attributes => TypeInfo->Attributes;
40 |
41 | public ulong AttributeCount => TypeInfo->AttributeCount;
42 |
43 | public V2017_3(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
44 | {
45 | TypeInfo = (RuntimeTypeInfo*)ptr;
46 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
47 | Name = TypeInfo->ClassName != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassName) : null;
48 | Namespace = TypeInfo->ClassNamespace != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassNamespace) : null;
49 | Module = TypeInfo->Module != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->Module) : null;
50 | }
51 |
52 | public ref byte GetPinnableReference()
53 | {
54 | return ref Unsafe.As(ref *TypeInfo);
55 | }
56 |
57 | internal unsafe struct RuntimeTypeInfo
58 | {
59 | public RuntimeTypeInfo* Base;
60 | public IntPtr Factory;
61 | public IntPtr ClassName;
62 | public IntPtr ClassNamespace;
63 | public IntPtr Module;
64 | public PersistentTypeID PersistentTypeID;
65 | public int Size;
66 | public DerivedFromInfo DerivedFromInfo;
67 | [MarshalAs(UnmanagedType.U1)]
68 | public bool IsAbstract;
69 | [MarshalAs(UnmanagedType.U1)]
70 | public bool IsSealed;
71 | [MarshalAs(UnmanagedType.U1)]
72 | public bool IsEditorOnly;
73 | [MarshalAs(UnmanagedType.U1)]
74 | public bool IsStripped;
75 | public IntPtr Attributes;
76 | public ulong AttributeCount;
77 | }
78 |
79 | internal struct DerivedFromInfo
80 | {
81 | public uint TypeIndex;
82 | public uint DescendantCount;
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V3_4.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using System.Text.RegularExpressions;
5 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
6 |
7 | namespace Unity
8 | {
9 | public partial class RuntimeTypeInfo
10 | {
11 | // Unity 3.4 - 4.7
12 | unsafe class V3_4 : IRuntimeTypeInfoImpl
13 | {
14 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
15 | unsafe delegate char* CStrDelegate(TypeTreeString* @this);
16 |
17 | RuntimeTypeInfo* TypeInfo;
18 |
19 | public ManagedRuntimeTypeInfo Base { get; }
20 |
21 | public string Name { get; }
22 |
23 | public string Namespace => null;
24 |
25 | public string Module => null;
26 |
27 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
28 |
29 | public int Size => TypeInfo->Size;
30 |
31 | public uint TypeIndex => 0;
32 |
33 | public uint DescendantCount => 0;
34 |
35 | public bool IsAbstract => TypeInfo->IsAbstract;
36 |
37 | public bool IsSealed => false;
38 |
39 | public bool IsEditorOnly => false;
40 |
41 | public bool IsStripped => false;
42 |
43 | public IntPtr Attributes => IntPtr.Zero;
44 |
45 | public ulong AttributeCount => 0;
46 |
47 |
48 | public V3_4(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
49 | {
50 | var cstr = (delegate* unmanaged[Thiscall])resolver.ResolveFirstMatch(
51 | new Regex(Regex.Escape("?c_str@?$basic_string@") + "*"));
52 | TypeInfo = (RuntimeTypeInfo*)ptr;
53 | var str = cstr(&TypeInfo->ClassName);
54 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
55 | Name = str != null ? Marshal.PtrToStringAnsi(new IntPtr(str)) : null;
56 | }
57 |
58 | public ref byte GetPinnableReference()
59 | {
60 | return ref Unsafe.As(ref *TypeInfo);
61 | }
62 |
63 | internal struct TypeTreeString
64 | {
65 | public fixed byte Data[28];
66 | }
67 |
68 | internal unsafe struct RuntimeTypeInfo
69 | {
70 | public RuntimeTypeInfo* Base;
71 | public IntPtr Factory;
72 | public PersistentTypeID PersistentTypeID;
73 | public TypeTreeString ClassName;
74 | public int Size;
75 | [MarshalAs(UnmanagedType.U1)]
76 | public bool IsAbstract;
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V5_0.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using System.Text.RegularExpressions;
5 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
6 |
7 | namespace Unity
8 | {
9 | public partial class RuntimeTypeInfo
10 | {
11 | // Unity 5.0 - 5.1
12 | unsafe class V5_0 : IRuntimeTypeInfoImpl
13 | {
14 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
15 | unsafe delegate char* CStrDelegate(TypeTreeString* @this);
16 |
17 | RuntimeTypeInfo* TypeInfo;
18 |
19 | public ManagedRuntimeTypeInfo Base { get; }
20 |
21 | public string Name { get; }
22 |
23 | public string Namespace => null;
24 |
25 | public string Module => null;
26 |
27 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
28 |
29 | public int Size => TypeInfo->Size;
30 |
31 | public uint TypeIndex => 0;
32 |
33 | public uint DescendantCount => 0;
34 |
35 | public bool IsAbstract => TypeInfo->IsAbstract;
36 |
37 | public bool IsSealed => false;
38 |
39 | public bool IsEditorOnly => false;
40 |
41 | public bool IsStripped => false;
42 |
43 | public IntPtr Attributes => IntPtr.Zero;
44 |
45 | public ulong AttributeCount => 0;
46 |
47 |
48 | public V5_0(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
49 | {
50 | var cstr = (delegate* unmanaged[Thiscall])resolver.ResolveFirstMatch(
51 | new Regex(Regex.Escape("?c_str@?$basic_string@") + "*"));
52 | TypeInfo = (RuntimeTypeInfo*)ptr;
53 | var str = cstr(&TypeInfo->ClassName);
54 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
55 | Name = str != null ? Marshal.PtrToStringAnsi(new IntPtr(str)) : null;
56 | }
57 |
58 | public ref byte GetPinnableReference()
59 | {
60 | return ref Unsafe.As(ref *TypeInfo);
61 | }
62 |
63 | internal struct TypeTreeString
64 | {
65 | public fixed byte Data[40];
66 | }
67 |
68 | internal unsafe struct RuntimeTypeInfo
69 | {
70 | public RuntimeTypeInfo* Base;
71 | public IntPtr Factory;
72 | public PersistentTypeID PersistentTypeID;
73 | public int Unknown;
74 | public TypeTreeString ClassName;
75 | public int Size;
76 | [MarshalAs(UnmanagedType.U1)]
77 | public bool IsAbstract;
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V5_2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
5 |
6 | namespace Unity
7 | {
8 | public partial class RuntimeTypeInfo
9 | {
10 | // Unity 5.2 - 5.3
11 | unsafe class V5_2 : IRuntimeTypeInfoImpl
12 | {
13 | RuntimeTypeInfo* TypeInfo;
14 |
15 | public ManagedRuntimeTypeInfo Base { get; }
16 |
17 | public string Name { get; }
18 |
19 | public string Namespace => null;
20 |
21 | public string Module => null;
22 |
23 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
24 |
25 | public int Size => TypeInfo->Size;
26 |
27 | public uint TypeIndex => 0;
28 |
29 | public uint DescendantCount => 0;
30 |
31 | public bool IsAbstract => TypeInfo->IsAbstract;
32 |
33 | public bool IsSealed => false;
34 |
35 | public bool IsEditorOnly => false;
36 |
37 | public bool IsStripped => false;
38 |
39 | public IntPtr Attributes => IntPtr.Zero;
40 |
41 | public ulong AttributeCount => 0;
42 |
43 |
44 | public V5_2(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
45 | {
46 | TypeInfo = (RuntimeTypeInfo*)ptr;
47 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
48 | Name = TypeInfo->ClassName != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassName) : null; }
49 |
50 | public ref byte GetPinnableReference()
51 | {
52 | return ref Unsafe.As(ref *TypeInfo);
53 | }
54 |
55 | internal unsafe struct RuntimeTypeInfo
56 | {
57 | public RuntimeTypeInfo* Base;
58 | public IntPtr Factory;
59 | public PersistentTypeID PersistentTypeID;
60 | public IntPtr ClassName;
61 | public int Size;
62 | [MarshalAs(UnmanagedType.U1)]
63 | public bool IsAbstract;
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V5_4.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
5 |
6 | namespace Unity
7 | {
8 | public partial class RuntimeTypeInfo
9 | {
10 | // Unity 5.4
11 | unsafe class V5_4 : IRuntimeTypeInfoImpl
12 | {
13 | RuntimeTypeInfo* TypeInfo;
14 |
15 | public ManagedRuntimeTypeInfo Base { get; }
16 |
17 | public string Name { get; }
18 |
19 | public string Namespace => null;
20 |
21 | public string Module => null;
22 |
23 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
24 |
25 | public int Size => TypeInfo->Size;
26 |
27 | public uint TypeIndex => TypeInfo->DerivedFromInfo.TypeIndex;
28 |
29 | public uint DescendantCount => TypeInfo->DerivedFromInfo.DescendantCount;
30 |
31 | public bool IsAbstract => TypeInfo->IsAbstract;
32 |
33 | public bool IsSealed => TypeInfo->IsSealed;
34 |
35 | public bool IsEditorOnly => TypeInfo->IsEditorOnly;
36 |
37 | public bool IsStripped => false;
38 |
39 | public IntPtr Attributes => IntPtr.Zero;
40 |
41 | public ulong AttributeCount => 0;
42 |
43 |
44 | public V5_4(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
45 | {
46 | TypeInfo = (RuntimeTypeInfo*)ptr;
47 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
48 | Name = TypeInfo->ClassName != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassName) : null;
49 | }
50 |
51 | public ref byte GetPinnableReference()
52 | {
53 | return ref Unsafe.As(ref *TypeInfo);
54 | }
55 |
56 | internal unsafe struct RuntimeTypeInfo
57 | {
58 | public RuntimeTypeInfo* Base;
59 | public IntPtr Factory;
60 | public IntPtr ClassName;
61 | public PersistentTypeID PersistentTypeID;
62 | public int Size;
63 | public DerivedFromInfo DerivedFromInfo;
64 | [MarshalAs(UnmanagedType.U1)]
65 | public bool IsAbstract;
66 | [MarshalAs(UnmanagedType.U1)]
67 | public bool IsSealed;
68 | [MarshalAs(UnmanagedType.U1)]
69 | public bool IsEditorOnly;
70 | }
71 |
72 | internal struct DerivedFromInfo
73 | {
74 | public uint TypeIndex;
75 | public uint DescendantCount;
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.V5_5.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using ManagedRuntimeTypeInfo = Unity.RuntimeTypeInfo;
5 |
6 | namespace Unity
7 | {
8 | public partial class RuntimeTypeInfo
9 | {
10 | // Unity 5.5 - 2017.2
11 | unsafe class V5_5 : IRuntimeTypeInfoImpl
12 | {
13 | RuntimeTypeInfo* TypeInfo;
14 |
15 | public ManagedRuntimeTypeInfo Base { get; }
16 |
17 | public string Name { get; }
18 |
19 | public string Namespace { get; }
20 |
21 | public string Module => null;
22 |
23 | public PersistentTypeID PersistentTypeID => TypeInfo->PersistentTypeID;
24 |
25 | public int Size => TypeInfo->Size;
26 |
27 | public uint TypeIndex => TypeInfo->DerivedFromInfo.TypeIndex;
28 |
29 | public uint DescendantCount => TypeInfo->DerivedFromInfo.DescendantCount;
30 |
31 | public bool IsAbstract => TypeInfo->IsAbstract;
32 |
33 | public bool IsSealed => TypeInfo->IsSealed;
34 |
35 | public bool IsEditorOnly => TypeInfo->IsEditorOnly;
36 |
37 | public bool IsStripped => false;
38 |
39 | public IntPtr Attributes => IntPtr.Zero;
40 |
41 | public ulong AttributeCount => 0;
42 |
43 |
44 | public V5_5(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
45 | {
46 | TypeInfo = (RuntimeTypeInfo*)ptr;
47 | Base = TypeInfo->Base != null ? new ManagedRuntimeTypeInfo(new IntPtr(TypeInfo->Base), resolver, version) : null;
48 | Name = TypeInfo->ClassName != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassName) : null;
49 | Namespace = TypeInfo->ClassNamespace != IntPtr.Zero ? Marshal.PtrToStringAnsi(TypeInfo->ClassNamespace) : null;
50 | }
51 |
52 | public ref byte GetPinnableReference()
53 | {
54 | return ref Unsafe.As(ref *TypeInfo);
55 | }
56 |
57 | internal unsafe struct RuntimeTypeInfo
58 | {
59 | public RuntimeTypeInfo* Base;
60 | public IntPtr Factory;
61 | public IntPtr ClassName;
62 | public IntPtr ClassNamespace;
63 | public PersistentTypeID PersistentTypeID;
64 | public int Size;
65 | public DerivedFromInfo DerivedFromInfo;
66 | [MarshalAs(UnmanagedType.U1)]
67 | public bool IsAbstract;
68 | [MarshalAs(UnmanagedType.U1)]
69 | public bool IsSealed;
70 | [MarshalAs(UnmanagedType.U1)]
71 | public bool IsEditorOnly;
72 | }
73 |
74 | internal struct DerivedFromInfo
75 | {
76 | public uint TypeIndex;
77 | public uint DescendantCount;
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/UnityInterop/RuntimeType/RuntimeTypeInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Unity
5 | {
6 | public partial class RuntimeTypeInfo
7 | {
8 | IRuntimeTypeInfoImpl TypeInfo;
9 |
10 | public ref byte GetPinnableReference()
11 | {
12 | return ref TypeInfo.GetPinnableReference();
13 | }
14 |
15 | public string Name => TypeInfo.Name;
16 |
17 | public string Namespace => TypeInfo.Namespace;
18 |
19 | public string FullName
20 | {
21 | get
22 | {
23 | if (string.IsNullOrEmpty(Namespace) || string.IsNullOrEmpty(Name)) return Name;
24 | else return $"{Namespace}.{Name}";
25 | }
26 | }
27 |
28 | public string Module => TypeInfo.Module;
29 |
30 | public PersistentTypeID PersistentTypeID => TypeInfo.PersistentTypeID;
31 |
32 | public RuntimeTypeInfo Base => TypeInfo.Base;
33 |
34 | public List Derived { get; } = new List();
35 |
36 | public int Size => TypeInfo.Size;
37 |
38 | public uint TypeIndex => TypeInfo.TypeIndex;
39 |
40 | public uint DescendantCount => TypeInfo.DescendantCount;
41 |
42 | public bool IsAbstract => TypeInfo.IsAbstract;
43 |
44 | public bool IsSealed => TypeInfo.IsSealed;
45 |
46 | public bool IsEditorOnly => TypeInfo.IsEditorOnly;
47 |
48 | public bool IsStripped => TypeInfo.IsStripped;
49 |
50 | public IntPtr Attributes => TypeInfo.Attributes;
51 |
52 | public ulong AttributeCount => TypeInfo.AttributeCount;
53 |
54 | internal RuntimeTypeInfo(IntPtr ptr, SymbolResolver resolver, UnityVersion version)
55 | {
56 | if (version >= UnityVersion.Unity2017_3)
57 | {
58 | TypeInfo = new V2017_3(ptr, resolver, version);
59 | }
60 | else if (version >= UnityVersion.Unity5_5)
61 | {
62 | TypeInfo = new V5_5(ptr, resolver, version);
63 | }
64 | else if (version >= UnityVersion.Unity5_4)
65 | {
66 | TypeInfo = new V5_4(ptr, resolver, version);
67 | }
68 | else if (version >= UnityVersion.Unity5_2)
69 | {
70 | TypeInfo = new V5_2(ptr, resolver, version);
71 | }
72 | else if (version >= UnityVersion.Unity5_0)
73 | {
74 | TypeInfo = new V5_0(ptr, resolver, version);
75 | }
76 | else
77 | {
78 | TypeInfo = new V3_4(ptr, resolver, version);
79 | }
80 | }
81 |
82 | interface IRuntimeTypeInfoImpl
83 | {
84 | RuntimeTypeInfo Base { get; }
85 | public string Name { get; }
86 | public string Namespace { get; }
87 | public string Module { get; }
88 | public PersistentTypeID PersistentTypeID { get; }
89 | public int Size { get; }
90 | public uint TypeIndex { get; }
91 | public uint DescendantCount { get; }
92 | public bool IsAbstract { get; }
93 | public bool IsSealed { get; }
94 | public bool IsEditorOnly { get; }
95 | public bool IsStripped { get; }
96 | public IntPtr Attributes { get; }
97 | public ulong AttributeCount { get; }
98 | ref byte GetPinnableReference();
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/UnityInterop/SymbolResolver.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Collections.Generic;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace Unity
6 | {
7 | public abstract unsafe class SymbolResolver
8 | {
9 | protected abstract void* GetAddressOrZero(string name);
10 |
11 | public abstract IEnumerable FindSymbolsMatching(Regex expression);
12 |
13 | public void* Resolve(string name)
14 | {
15 | if (TryResolve(name, out void* address))
16 | return address;
17 |
18 | throw new UnresolvedSymbolException(name);
19 | }
20 |
21 | public void* Resolve(params string[] names)
22 | {
23 | foreach (string name in names)
24 | {
25 | if (TryResolve(name, out void* address))
26 | return address;
27 | }
28 |
29 | throw new UnresolvedSymbolException(string.Join(", ", names));
30 | }
31 |
32 | public bool TryResolve(string name, out void* address)
33 | {
34 | address = GetAddressOrZero(name);
35 | return address != null;
36 | }
37 |
38 | public unsafe bool TryResolve(string name, out T* address)
39 | where T : unmanaged
40 | {
41 | address = (T*)GetAddressOrZero(name);
42 | return address != null;
43 | }
44 |
45 | public unsafe T* Resolve(string name)
46 | where T : unmanaged
47 | {
48 | if (TryResolve(name, out T* address))
49 | return address;
50 |
51 | throw new UnresolvedSymbolException(name);
52 | }
53 |
54 | public unsafe T* Resolve(params string[] names)
55 | where T : unmanaged
56 | {
57 | foreach (string name in names)
58 | {
59 | if (TryResolve(name, out T* address))
60 | return address;
61 | }
62 |
63 | throw new UnresolvedSymbolException(string.Join(", ", names));
64 | }
65 |
66 | public bool TryResolveFirstMatch(Regex regex, out void* address)
67 | {
68 | var name = FindSymbolsMatching(regex).FirstOrDefault();
69 |
70 | if (string.IsNullOrEmpty(name))
71 | {
72 | address = null;
73 | return false;
74 | }
75 |
76 | address = GetAddressOrZero(name);
77 | return address != null;
78 | }
79 |
80 | public unsafe bool TryResolveFirstMatch(Regex regex, out T* address)
81 | where T : unmanaged
82 | {
83 | bool success = TryResolveFirstMatch(regex, out void* ptr);
84 | address = (T*)ptr;
85 | return success;
86 | }
87 |
88 | public void* ResolveFirstMatch(Regex regex)
89 | {
90 | var name = FindSymbolsMatching(regex).FirstOrDefault();
91 |
92 | if (string.IsNullOrEmpty(name))
93 | throw new UnresolvedSymbolException(regex.ToString());
94 |
95 | return GetAddressOrZero(name);
96 | }
97 |
98 | public void* ResolveFirstMatch(params Regex[] regexes)
99 | {
100 | string name = null;
101 |
102 | foreach (var regex in regexes)
103 | {
104 | name = FindSymbolsMatching(regex).FirstOrDefault();
105 |
106 | if (name != null)
107 | {
108 | break;
109 | }
110 | }
111 |
112 | if (string.IsNullOrEmpty(name))
113 | throw new UnresolvedSymbolException(string.Join(", ", (object[])regexes));
114 |
115 | return GetAddressOrZero(name);
116 | }
117 |
118 | public unsafe T* ResolveFirstMatch(Regex regex)
119 | where T : unmanaged
120 | {
121 | return (T*)ResolveFirstMatch(regex);
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/UnityInterop/TransferInstructionFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | [Flags]
6 | public enum TransferInstructionFlags
7 | {
8 | None = 0,
9 | ReadWriteFromSerializedFile = 1 << 0,
10 | AssetMetaDataOnly = 1 << 1,
11 | HandleDrivenProperties = 1 << 2,
12 | LoadAndUnloadAssetsDuringBuild = 1 << 3,
13 | SerializeDebugProperties = 1 << 4,
14 | IgnoreDebugPropertiesForIndex = 1 << 5,
15 | BuildPlayerOnlySerializeBuildProperties = 1 << 6,
16 | IsCloningObject = 1 << 7,
17 | SerializeGameRelease = 1 << 8,
18 | SwapEndianess = 1 << 9,
19 | ResolveStreamedResourceSources = 1 << 10,
20 | DontReadObjectsFromDiskBeforeWriting = 1 << 11,
21 | SerializeMonoReload = 1 << 12,
22 | DontRequireAllMetaFlags = 1 << 13,
23 | SerializeForPrefabSystem = 1 << 14,
24 | WarnAboutLeakedObjects = 1 << 15,
25 | LoadPrefabAsScene = 1 << 16,
26 | SerializeCopyPasteTransfer = 1 << 17,
27 | EditorPlayMode = 1 << 18,
28 | BuildResourceImage = 1 << 19,
29 | SerializeEditorMinimalScene = 1 << 21,
30 | GenerateBakedPhysixMeshes = 1 << 22,
31 | ThreadedSerialization = 1 << 23,
32 | IsBuiltinResourcesFile = 1 << 24,
33 | PerformUnloadDependencyTracking = 1 << 25,
34 | DisableWriteTypeTree = 1 << 26,
35 | AutoreplaceEditorWindow = 1 << 27,
36 | DontCreateMonoBehaviourScriptWrapper = 1 << 28,
37 | SerializeForInspector = 1 << 29,
38 | SerializedAssetBundleVersion = 1 << 30,
39 | AllowTextSerialization = 1 << 31,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/UnityInterop/TransferMetaFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | [Flags]
6 | public enum TransferMetaFlags : uint
7 | {
8 | None = 0,
9 | HideInEditor = 1 << 0,
10 | NotEditable = 1 << 4,
11 | StrongPPtr = 1 << 6,
12 | TreatIntegerValueAsBoolean = 1 << 8,
13 | SimpleEditor = 1 << 11,
14 | DebugProperty = 1 << 12,
15 | AlignBytes = 1 << 14,
16 | AnyChildUsesAlignBytesFlag = 1 << 15,
17 | IgnoreWithInspectorUndo = 1 << 16,
18 | EditorDisplaysCharacterMap = 1 << 18,
19 | IgnoreInMetaFiles = 1 << 19,
20 | TransferAsArrayEntryNameInMetaFiles = 1 << 20,
21 | TransferUsingFlowMappingStyle = 1 << 21,
22 | GenerateBitwiseDifferences = 1 << 22,
23 | DontAnimate = 1 << 23,
24 | TransferHex64 = 1 << 24,
25 | CharPropertyMask = 1 << 25,
26 | DontValidateUTF8 = 1 << 26,
27 | FixedBuffer = 1 << 27,
28 | DisallowSerializedPropertyModification = 1 << 28,
29 | }
30 |
31 | public static class TransferMetaFlagsExtensions
32 | {
33 | public static bool IsHideInEditor(this TransferMetaFlags _this) => (_this & TransferMetaFlags.HideInEditor) != 0;
34 | public static bool IsNotEditable(this TransferMetaFlags _this) => (_this & TransferMetaFlags.NotEditable) != 0;
35 | public static bool IsStrongPPtr(this TransferMetaFlags _this) => (_this & TransferMetaFlags.StrongPPtr) != 0;
36 | public static bool IsTreatIntegerValueAsBoolean(this TransferMetaFlags _this) => (_this & TransferMetaFlags.TreatIntegerValueAsBoolean) != 0;
37 | public static bool IsSimpleEditor(this TransferMetaFlags _this) => (_this & TransferMetaFlags.SimpleEditor) != 0;
38 | public static bool IsDebugProperty(this TransferMetaFlags _this) => (_this & TransferMetaFlags.DebugProperty) != 0;
39 | public static bool IsAlignBytes(this TransferMetaFlags _this) => (_this & TransferMetaFlags.AlignBytes) != 0;
40 | public static bool IsAnyChildUsesAlignBytesFlag(this TransferMetaFlags _this) => (_this & TransferMetaFlags.AnyChildUsesAlignBytesFlag) != 0;
41 | public static bool IsIgnoreWithInspectorUndo(this TransferMetaFlags _this) => (_this & TransferMetaFlags.IgnoreWithInspectorUndo) != 0;
42 | public static bool IsEditorDisplaysCharacterMap(this TransferMetaFlags _this) => (_this & TransferMetaFlags.EditorDisplaysCharacterMap) != 0;
43 | public static bool IsIgnoreInMetaFiles(this TransferMetaFlags _this) => (_this & TransferMetaFlags.IgnoreInMetaFiles) != 0;
44 | public static bool IsTransferAsArrayEntryNameInMetaFiles(this TransferMetaFlags _this) => (_this & TransferMetaFlags.TransferAsArrayEntryNameInMetaFiles) != 0;
45 | public static bool IsTransferUsingFlowMappingStyle(this TransferMetaFlags _this) => (_this & TransferMetaFlags.TransferUsingFlowMappingStyle) != 0;
46 | public static bool IsGenerateBitwiseDifferences(this TransferMetaFlags _this) => (_this & TransferMetaFlags.GenerateBitwiseDifferences) != 0;
47 | public static bool IsDontAnimate(this TransferMetaFlags _this) => (_this & TransferMetaFlags.DontAnimate) != 0;
48 | public static bool IsTransferHex64(this TransferMetaFlags _this) => (_this & TransferMetaFlags.TransferHex64) != 0;
49 | public static bool IsCharPropertyMask(this TransferMetaFlags _this) => (_this & TransferMetaFlags.CharPropertyMask) != 0;
50 | public static bool IsDontValidateUTF8(this TransferMetaFlags _this) => (_this & TransferMetaFlags.DontValidateUTF8) != 0;
51 | public static bool IsFixedBuffer(this TransferMetaFlags _this) => (_this & TransferMetaFlags.FixedBuffer) != 0;
52 | public static bool IsDisallowSerializedPropertyModification(this TransferMetaFlags _this) => (_this & TransferMetaFlags.DisallowSerializedPropertyModification) != 0;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/UnityInterop/TypeFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | [Flags]
6 | public enum TypeFlags : byte
7 | {
8 | None = 0,
9 | IsArray = 1 << 0,
10 | IsManagedReference = 1 << 1,
11 | IsManagedReferenceRegistry = 1 << 2,
12 | IsArrayOfRefs = 1 << 3,
13 | }
14 |
15 | public static class TypeFlagsExtensions
16 | {
17 | public static bool IsArray(this TypeFlags _this)
18 | {
19 | return (_this & TypeFlags.IsArray) != 0;
20 | }
21 | public static bool IsManagedReference(this TypeFlags _this)
22 | {
23 | return (_this & TypeFlags.IsManagedReference) != 0;
24 | }
25 | public static bool IsManagedReferenceRegistry(this TypeFlags _this)
26 | {
27 | return (_this & TypeFlags.IsManagedReferenceRegistry) != 0;
28 | }
29 | public static bool IsArrayOfRefs(this TypeFlags _this)
30 | {
31 | return (_this & TypeFlags.IsArrayOfRefs) != 0;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V2019_1.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 |
6 | namespace Unity
7 | {
8 | public partial class TypeTree
9 | {
10 | // Unity 2019.1 - 2019.2
11 | unsafe class V2019_1 : ITypeTreeImpl
12 | {
13 | internal TypeTree Tree;
14 |
15 | public IReadOnlyList StringBuffer => Tree.Data->StringBuffer;
16 |
17 | public IReadOnlyList ByteOffsets => Tree.Data->ByteOffsets;
18 |
19 | public IReadOnlyList Nodes => m_Nodes;
20 |
21 | private TypeTreeNode[] m_Nodes;
22 |
23 | public V2019_1(ManagedTypeTree owner, SymbolResolver resolver)
24 | {
25 | var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@A{NameMangling.Ptr64}BUMemLabelId@@_N@Z");
26 | var label = resolver.Resolve("?kMemTypeTree@@3UMemLabelId@@A");
27 | TypeTree tree;
28 | constructor(&tree, label, 0);
29 | Tree = tree;
30 | }
31 |
32 | public ref byte GetPinnableReference()
33 | {
34 | return ref Unsafe.As(ref Tree);
35 | }
36 |
37 | public void CreateNodes(ManagedTypeTree owner)
38 | {
39 | var nodes = new TypeTreeNode[Tree.Data->Nodes.Size];
40 |
41 | for (int i = 0; i < nodes.Length; i++)
42 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V2019_1(Tree.Data->Nodes.Ptr[i]), owner);
43 |
44 | m_Nodes = nodes;
45 | }
46 |
47 | internal struct TypeTree
48 | {
49 | public TypeTreeShareableData* Data;
50 | public TypeTreeShareableData PrivateData;
51 | }
52 |
53 | internal struct TypeTreeShareableData
54 | {
55 | public DynamicArray Nodes;
56 | public DynamicArray StringBuffer;
57 | public DynamicArray ByteOffsets;
58 | public TransferInstructionFlags FlagsAtGeneration;
59 | public int RefCount;
60 | public MemLabelId* MemLabel;
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V2019_3.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.CompilerServices;
5 | using ManagedTypeTree = Unity.TypeTree;
6 |
7 | namespace Unity
8 | {
9 | public partial class TypeTree
10 | {
11 | // Unity 2019.3 - 2022.1
12 | unsafe class V2019_3 : ITypeTreeImpl
13 | {
14 | internal TypeTree Tree;
15 |
16 | public IReadOnlyList StringBuffer => Tree.Data->StringBuffer;
17 |
18 | public IReadOnlyList ByteOffsets => Tree.Data->ByteOffsets;
19 |
20 | public IReadOnlyList Nodes => m_Nodes;
21 |
22 | private TypeTreeNode[] m_Nodes;
23 |
24 | public V2019_3(ManagedTypeTree owner, SymbolResolver resolver)
25 | {
26 | var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@A{NameMangling.Ptr64}BUMemLabelId@@@Z");
27 | var label = resolver.Resolve("?kMemTypeTree@@3UMemLabelId@@A");
28 | TypeTree tree;
29 | constructor(&tree, label);
30 | Tree = tree;
31 | }
32 |
33 | public ref byte GetPinnableReference()
34 | {
35 | return ref Unsafe.As(ref Tree);
36 | }
37 |
38 | public void CreateNodes(ManagedTypeTree owner)
39 | {
40 | var nodes = new TypeTreeNode[Tree.Data->Nodes.Size];
41 |
42 | for (int i = 0; i < nodes.Length; i++)
43 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V2019_1(Tree.Data->Nodes.Ptr[i]), owner);
44 | m_Nodes = nodes;
45 | }
46 |
47 | internal struct TypeTree
48 | {
49 | public TypeTreeShareableData* Data;
50 | public IntPtr ReferencedTypes;
51 | [MarshalAs(UnmanagedType.U1)]
52 | public bool PoolOwned;
53 | }
54 |
55 | internal struct TypeTreeShareableData
56 | {
57 | public DynamicArray Nodes;
58 | public DynamicArray StringBuffer;
59 | public DynamicArray ByteOffsets;
60 | public TransferInstructionFlags FlagsAtGeneration;
61 | public int RefCount;
62 | public MemLabelId* MemLabel;
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V2022_2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.CompilerServices;
5 | using ManagedTypeTree = Unity.TypeTree;
6 |
7 | namespace Unity
8 | {
9 | public partial class TypeTree
10 | {
11 | // Unity 2022.2+
12 | unsafe class V2022_2 : ITypeTreeImpl
13 | {
14 | internal TypeTree Tree;
15 |
16 | public IReadOnlyList StringBuffer => Tree.Data->StringBuffer;
17 |
18 | public IReadOnlyList ByteOffsets => Tree.Data->ByteOffsets;
19 |
20 | public IReadOnlyList Nodes => m_Nodes;
21 |
22 | private TypeTreeNode[] m_Nodes;
23 |
24 | public V2022_2(ManagedTypeTree owner, SymbolResolver resolver)
25 | {
26 | //var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@A{NameMangling.Ptr64}BUMemLabelId@@@Z");
27 | //MemLabelId label = MemLabelId.DefaultMemTypeTree_2020_2_6;
28 | //TypeTree tree;
29 | //constructor(&tree, &label);
30 | //Tree = tree;
31 |
32 | //Strangely, the constructor seems unnecessary even though it's still present on this version.
33 | Tree = default;
34 | }
35 |
36 | public ref byte GetPinnableReference()
37 | {
38 | return ref Unsafe.As(ref Tree);
39 | }
40 |
41 | public void CreateNodes(ManagedTypeTree owner)
42 | {
43 | var nodes = new TypeTreeNode[Tree.Data->Nodes.Size];
44 |
45 | for (int i = 0; i < nodes.Length; i++)
46 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V2019_1(Tree.Data->Nodes.Ptr[i]), owner);
47 | m_Nodes = nodes;
48 | }
49 |
50 | internal struct TypeTree
51 | {
52 | public TypeTreeShareableData* Data;
53 | public IntPtr ReferencedTypes;
54 | [MarshalAs(UnmanagedType.U1)]
55 | public bool PoolOwned;
56 | }
57 |
58 | internal struct TypeTreeShareableData
59 | {
60 | public DynamicArray Nodes;
61 | public DynamicArray Levels;
62 | public DynamicArray NextIndex;
63 | public DynamicArray StringBuffer;
64 | public DynamicArray ByteOffsets;
65 | public TransferInstructionFlags FlagsAtGeneration;
66 | public int RefCount;
67 | public MemLabelId* MemLabel;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V2023_1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.CompilerServices;
5 | using ManagedTypeTree = Unity.TypeTree;
6 |
7 | namespace Unity
8 | {
9 | public partial class TypeTree
10 | {
11 | // Unity 2023.1.0a2+
12 | unsafe class V2023_1 : ITypeTreeImpl
13 | {
14 | internal TypeTree Tree;
15 |
16 | public IReadOnlyList StringBuffer => Tree.Data->StringBuffer;
17 |
18 | public IReadOnlyList ByteOffsets => Tree.Data->ByteOffsets;
19 |
20 | public IReadOnlyList Nodes => m_Nodes;
21 |
22 | private TypeTreeNode[] m_Nodes;
23 |
24 | public V2023_1(ManagedTypeTree owner, SymbolResolver resolver)
25 | {
26 | //var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@A{NameMangling.Ptr64}BUMemLabelId@@@Z");
27 | //MemLabelId label = MemLabelId.DefaultMemTypeTree_2020_2_6;
28 | //TypeTree tree;
29 | //constructor(&tree, &label);
30 | //Tree = tree;
31 |
32 | //Strangely, the constructor seems unnecessary even though it's still present on this version.
33 | Tree = default;
34 | }
35 |
36 | public ref byte GetPinnableReference()
37 | {
38 | return ref Unsafe.As(ref Tree);
39 | }
40 |
41 | public void CreateNodes(ManagedTypeTree owner)
42 | {
43 | var nodes = new TypeTreeNode[Tree.Data->Nodes.Size];
44 |
45 | for (int i = 0; i < nodes.Length; i++)
46 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V2019_1(Tree.Data->Nodes.Ptr[i]), owner);
47 | m_Nodes = nodes;
48 | }
49 |
50 | internal struct TypeTree
51 | {
52 | public TypeTreeShareableData* Data;
53 | public IntPtr ReferencedTypes;
54 | [MarshalAs(UnmanagedType.U1)]
55 | public bool PoolOwned;
56 | }
57 |
58 | internal struct TypeTreeShareableData
59 | {
60 | public DynamicArray Nodes;
61 | public DynamicArray Levels;
62 | public DynamicArray NextIndex;
63 | public DynamicArray StringBuffer;
64 | public DynamicArray ByteOffsets;
65 | public TransferInstructionFlags FlagsAtGeneration;
66 | public int RefCount;
67 | public MemoryLabelIdentifier* MemLabel;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V3_4.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 | using System;
6 | using System.Text.RegularExpressions;
7 | using System.Linq;
8 |
9 | namespace Unity
10 | {
11 | public partial class TypeTree
12 | {
13 | unsafe class V3_4 : ITypeTreeImpl
14 | {
15 | readonly delegate* unmanaged[Thiscall] s_CStr;
16 |
17 | internal TypeTree Tree;
18 |
19 | public IReadOnlyList StringBuffer => m_StringBuffer;
20 |
21 | public IReadOnlyList Nodes => m_Nodes;
22 |
23 | public IReadOnlyList ByteOffsets => m_ByteOffsets;
24 |
25 | private List m_ByteOffsets;
26 |
27 | private List m_Nodes;
28 |
29 | private List m_StringBuffer;
30 |
31 | private Dictionary m_StringBufferIndices;
32 |
33 |
34 | public V3_4(ManagedTypeTree owner, SymbolResolver resolver)
35 | {
36 | TypeTree tree;
37 | var constructor = (delegate* unmanaged[Thiscall])resolver.Resolve($"??0TypeTree@@QAE@XZ");
38 | constructor(&tree);
39 | Tree = tree;
40 |
41 | s_CStr = (delegate* unmanaged[Thiscall])resolver.ResolveFirstMatch(
42 | new Regex(Regex.Escape("?c_str@?$basic_string@") + "*"));
43 | }
44 |
45 | public ref byte GetPinnableReference()
46 | {
47 | return ref Unsafe.As(ref Tree);
48 | }
49 |
50 | public void CreateNodes(ManagedTypeTree owner)
51 | {
52 | m_StringBuffer = new List();
53 | m_StringBufferIndices = new Dictionary();
54 | m_Nodes = new List();
55 | m_ByteOffsets = new List();
56 | CreateNodes(owner, ref m_Nodes, ref Tree);
57 | }
58 |
59 | public void CreateNodes(ManagedTypeTree owner, ref List nodes, ref TypeTree tree, int level = 0)
60 | {
61 | var typeIndex = GetOrCreateStringIndex(tree.m_Type);
62 | var type = m_StringBufferIndices.First(kv => kv.Value == typeIndex).Key;
63 | var nameIndex = GetOrCreateStringIndex(tree.m_Name);
64 | var name = m_StringBufferIndices.First(kv => kv.Value == nameIndex).Key;
65 | var nodeImpl = new TypeTreeNode.V1(
66 | version: (short)tree.m_Version,
67 | level: (byte)level,
68 | typeFlags: (TypeFlags)tree.m_IsArray,
69 | typeStrOffset: typeIndex,
70 | nameStrOffset: nameIndex,
71 | byteSize: tree.m_ByteSize,
72 | index: tree.m_Index,
73 | metaFlag: tree.m_MetaFlag);
74 | nodes.Add(new TypeTreeNode(nodeImpl, owner));
75 | m_ByteOffsets.Add((uint)tree.m_ByteOffset);
76 |
77 | var node = tree.m_Children.Head;
78 | for(int i = 0; i < tree.m_Children.Size; i++)
79 | {
80 | node = node->Next;
81 | var child = node->Value;
82 | CreateNodes(owner, ref nodes, ref child, level + 1);
83 | }
84 | }
85 |
86 | uint GetOrCreateStringIndex(TypeTreeString typeTreeString)
87 | {
88 | var tts = typeTreeString;
89 | var str = Marshal.PtrToStringAnsi((IntPtr)s_CStr(&typeTreeString));
90 | if (m_StringBufferIndices.TryGetValue(str, out var key))
91 | {
92 | return key;
93 | }
94 | var newKey = (uint)m_StringBuffer.Count;
95 | m_StringBufferIndices[str] = newKey;
96 | foreach (byte b in str)
97 | {
98 | m_StringBuffer.Add(b);
99 | }
100 | m_StringBuffer.Add(0);
101 | return newKey;
102 | }
103 |
104 | [StructLayout(LayoutKind.Explicit)]
105 | internal struct TypeTreeString
106 | {
107 | [FieldOffset(0)]
108 | public uint Unknown;
109 | [FieldOffset(4)]
110 | fixed byte Buffer[16];
111 | [FieldOffset(4)]
112 | IntPtr Ptr;
113 | [FieldOffset(20)]
114 | public uint Size;
115 | [FieldOffset(24)]
116 | public uint Reserved;
117 |
118 | public override string ToString()
119 | {
120 | if (Size < 16)
121 | {
122 | string result = "";
123 | for(int i = 0; i < Size; i++)
124 | {
125 | result += (char)Buffer[i];
126 | }
127 | return result;
128 | } else
129 | {
130 | return Marshal.PtrToStringAnsi(Ptr);
131 | }
132 | }
133 | };
134 |
135 | internal unsafe struct TypeTreeList
136 | {
137 | public uint Unknown1;
138 | public TypeTreeListNode* Head;
139 | public int Size;
140 | };
141 |
142 | internal unsafe struct TypeTreeListNode
143 | {
144 | public TypeTreeListNode* Next;
145 | public TypeTreeListNode* Prev;
146 | public TypeTree Value;
147 | }
148 |
149 | internal struct TypeTree
150 | {
151 | public TypeTreeList m_Children;
152 | public TypeTree* m_Father;
153 | public TypeTreeString m_Type;
154 | public TypeTreeString m_Name;
155 | public int m_ByteSize;
156 | public int m_Index;
157 | public int m_IsArray;
158 | public int m_Version;
159 | public TransferMetaFlags m_MetaFlag;
160 | public int m_ByteOffset;
161 | public void* m_DirectPtr;
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V3_5.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 | using System;
6 | using System.Text.RegularExpressions;
7 | using System.Linq;
8 |
9 | namespace Unity
10 | {
11 | public partial class TypeTree
12 | {
13 | unsafe class V3_5 : ITypeTreeImpl
14 | {
15 | readonly delegate* unmanaged[Thiscall] s_CStr;
16 |
17 | internal TypeTree Tree;
18 |
19 | public IReadOnlyList StringBuffer => m_StringBuffer;
20 |
21 | public IReadOnlyList Nodes => m_Nodes;
22 |
23 | public IReadOnlyList ByteOffsets => m_ByteOffsets;
24 |
25 | private List m_ByteOffsets;
26 |
27 | private List m_Nodes;
28 |
29 | private List m_StringBuffer;
30 |
31 | private Dictionary m_StringBufferIndices;
32 |
33 |
34 | public V3_5(ManagedTypeTree owner, SymbolResolver resolver)
35 | {
36 | TypeTree tree;
37 | var constructor = (delegate* unmanaged[Thiscall])resolver.Resolve("??0TypeTree@@QAE@XZ");
38 | constructor(&tree);
39 | Tree = tree;
40 |
41 | s_CStr = (delegate* unmanaged[Thiscall])resolver.ResolveFirstMatch(
42 | new Regex(Regex.Escape("?c_str@?$basic_string@") + "*"));
43 | }
44 |
45 | public ref byte GetPinnableReference()
46 | {
47 | return ref Unsafe.As(ref Tree);
48 | }
49 |
50 | public void CreateNodes(ManagedTypeTree owner)
51 | {
52 | m_StringBuffer = new List();
53 | m_StringBufferIndices = new Dictionary();
54 | m_Nodes = new List();
55 | m_ByteOffsets = new List();
56 | CreateNodes(owner, ref m_Nodes, ref Tree);
57 | }
58 |
59 | public void CreateNodes(ManagedTypeTree owner, ref List nodes, ref TypeTree tree, int level = 0)
60 | {
61 | var typeIndex = GetOrCreateStringIndex(tree.m_Type);
62 | var type = m_StringBufferIndices.First(kv => kv.Value == typeIndex).Key;
63 | var nameIndex = GetOrCreateStringIndex(tree.m_Name);
64 | var name = m_StringBufferIndices.First(kv => kv.Value == nameIndex).Key;
65 | var nodeImpl = new TypeTreeNode.V1(
66 | version: (short)tree.m_Version,
67 | level: (byte)level,
68 | typeFlags: (TypeFlags)tree.m_IsArray,
69 | typeStrOffset: typeIndex,
70 | nameStrOffset: nameIndex,
71 | byteSize: tree.m_ByteSize,
72 | index: tree.m_Index,
73 | metaFlag: tree.m_MetaFlag);
74 | nodes.Add(new TypeTreeNode(nodeImpl, owner));
75 | m_ByteOffsets.Add((uint)tree.m_ByteOffset);
76 |
77 | var node = tree.m_Children.Head;
78 | for(int i = 0; i < tree.m_Children.Size; i++)
79 | {
80 | node = node->Next;
81 | var child = node->Value;
82 | CreateNodes(owner, ref nodes, ref child, level + 1);
83 | }
84 | }
85 |
86 | uint GetOrCreateStringIndex(TypeTreeString typeTreeString)
87 | {
88 | var tts = typeTreeString;
89 | var str = Marshal.PtrToStringAnsi((IntPtr)s_CStr(&tts));
90 | if (m_StringBufferIndices.TryGetValue(str, out var key))
91 | {
92 | return key;
93 | }
94 | var newKey = (uint)m_StringBuffer.Count;
95 | m_StringBufferIndices[str] = newKey;
96 | foreach (byte b in str)
97 | {
98 | m_StringBuffer.Add(b);
99 | }
100 | m_StringBuffer.Add(0);
101 | return newKey;
102 | }
103 |
104 | [StructLayout(LayoutKind.Explicit)]
105 | internal struct TypeTreeString
106 | {
107 | [FieldOffset(0)]
108 | public uint Unknown;
109 | [FieldOffset(4)]
110 | fixed byte Buffer[16];
111 | [FieldOffset(4)]
112 | IntPtr Ptr;
113 | [FieldOffset(20)]
114 | public uint Size;
115 | [FieldOffset(24)]
116 | public uint Reserved;
117 |
118 | public override string ToString()
119 | {
120 | if (Size < 16)
121 | {
122 | string result = "";
123 | for(int i = 0; i < Size; i++)
124 | {
125 | result += (char)Buffer[i];
126 | }
127 | return result;
128 | } else
129 | {
130 | return Marshal.PtrToStringAnsi(Ptr);
131 | }
132 | }
133 | };
134 |
135 | internal unsafe struct TypeTreeList
136 | {
137 | public uint Unknown1;
138 | public uint Unknown2;
139 | public uint Unknown3;
140 | public TypeTreeListNode* Head;
141 | public int Size;
142 | };
143 |
144 | internal unsafe struct TypeTreeListNode
145 | {
146 | public TypeTreeListNode* Next;
147 | public TypeTreeListNode* Prev;
148 | public TypeTree Value;
149 | }
150 |
151 | internal struct TypeTree
152 | {
153 | public TypeTreeList m_Children;
154 | public TypeTree* m_Father;
155 | public TypeTreeString m_Type;
156 | public TypeTreeString m_Name;
157 | public int m_ByteSize;
158 | public int m_Index;
159 | public int m_IsArray;
160 | public int m_Version;
161 | public TransferMetaFlags m_MetaFlag;
162 | public int m_ByteOffset;
163 | public void* m_DirectPtr;
164 | }
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V4_0.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 | using System;
6 | using System.Text.RegularExpressions;
7 | using System.Linq;
8 | using System.IO;
9 |
10 | namespace Unity
11 | {
12 | public partial class TypeTree
13 | {
14 | unsafe class V4_0 : ITypeTreeImpl
15 | {
16 | readonly delegate* unmanaged[Thiscall] s_CStr;
17 |
18 | internal TypeTree Tree;
19 |
20 | public IReadOnlyList StringBuffer => m_StringBuffer;
21 |
22 | public IReadOnlyList Nodes => m_Nodes;
23 |
24 | public IReadOnlyList ByteOffsets => m_ByteOffsets;
25 |
26 | private List m_ByteOffsets;
27 |
28 | private List m_Nodes;
29 |
30 | private List m_StringBuffer;
31 |
32 | private Dictionary m_StringBufferIndices;
33 |
34 | private StreamWriter sw;
35 |
36 | public V4_0(ManagedTypeTree owner, SymbolResolver resolver)
37 | {
38 | TypeTree tree;
39 | var constructor = (delegate* unmanaged[Thiscall])resolver.Resolve("??0TypeTree@@QAE@XZ");
40 | constructor(&tree);
41 | Tree = tree;
42 |
43 | s_CStr = (delegate* unmanaged[Thiscall])resolver.ResolveFirstMatch(
44 | new Regex(Regex.Escape("?c_str@?$basic_string@") + "*"));
45 | }
46 |
47 | public ref byte GetPinnableReference()
48 | {
49 | return ref Unsafe.As(ref Tree);
50 | }
51 |
52 | public void CreateNodes(ManagedTypeTree owner)
53 | {
54 | //Debugger.Launch();
55 | m_StringBuffer = new List();
56 | m_StringBufferIndices = new Dictionary();
57 | m_Nodes = new List();
58 | m_ByteOffsets = new List();
59 | var tts = Tree.m_Type;
60 | var type = Marshal.PtrToStringAnsi((IntPtr)s_CStr(&tts));
61 | sw = new StreamWriter($"{type}.txt");
62 | CreateNodes(owner, ref m_Nodes, ref Tree);
63 | sw.Dispose();
64 | }
65 |
66 | public void CreateNodes(ManagedTypeTree owner, ref List nodes, ref TypeTree tree, int level = 0)
67 | {
68 | var typeIndex = GetOrCreateStringIndex(tree.m_Type);
69 | var type = m_StringBufferIndices.First(kv => kv.Value == typeIndex).Key;
70 | var nameIndex = GetOrCreateStringIndex(tree.m_Name);
71 | var name = m_StringBufferIndices.First(kv => kv.Value == nameIndex).Key;
72 | var nodeImpl = new TypeTreeNode.V1(
73 | version: (short)tree.m_Version,
74 | level: (byte)level,
75 | typeFlags: (TypeFlags)tree.m_IsArray,
76 | typeStrOffset: typeIndex,
77 | nameStrOffset: nameIndex,
78 | byteSize: tree.m_ByteSize,
79 | index: tree.m_Index,
80 | metaFlag: tree.m_MetaFlag);
81 | nodes.Add(new TypeTreeNode(nodeImpl, owner));
82 | m_ByteOffsets.Add((uint)tree.m_ByteOffset);
83 | sw.WriteLine("{0}Type: {1} Name: {2}: Children: {3} Padding1: {4} Padding2: {5}",
84 | new string(' ', level),
85 | type,
86 | name,
87 | tree.m_Children.Size,
88 | tree.m_Children.Padding1,
89 | tree.m_Children.Padding2);
90 | var node = tree.m_Children.Head;
91 | for(int i = 0; i < tree.m_Children.Size; i++)
92 | {
93 | node = node->Next;
94 | var child = node->Value;
95 | CreateNodes(owner, ref nodes, ref child, level + 1);
96 | }
97 | }
98 |
99 | uint GetOrCreateStringIndex(TypeTreeString typeTreeString)
100 | {
101 | var tts = typeTreeString;
102 | var str = Marshal.PtrToStringAnsi((IntPtr)s_CStr(&tts));
103 | if (m_StringBufferIndices.TryGetValue(str, out var key))
104 | {
105 | return key;
106 | }
107 | var newKey = (uint)m_StringBuffer.Count;
108 | m_StringBufferIndices[str] = newKey;
109 | foreach (byte b in str)
110 | {
111 | m_StringBuffer.Add(b);
112 | }
113 | m_StringBuffer.Add(0);
114 | return newKey;
115 | }
116 |
117 | [StructLayout(LayoutKind.Explicit)]
118 | internal struct TypeTreeString
119 | {
120 | [FieldOffset(0)]
121 | fixed byte Buffer[16];
122 | [FieldOffset(0)]
123 | IntPtr Ptr;
124 | [FieldOffset(16)]
125 | uint Size;
126 | [FieldOffset(20)]
127 | uint Reserved;
128 | [FieldOffset(24)]
129 | uint Padding;
130 |
131 | public override string ToString()
132 | {
133 | if (Size < 16)
134 | {
135 | string result = "";
136 | for(int i = 0; i < Size; i++)
137 | {
138 | result += (char)Buffer[i];
139 | }
140 | return result;
141 | } else
142 | {
143 | return Marshal.PtrToStringAnsi(Ptr);
144 | }
145 | }
146 | };
147 |
148 | internal unsafe struct TypeTreeList
149 | {
150 | public TypeTreeListNode* Head;
151 | public uint Size;
152 | public int Padding1;
153 | public int Padding2;
154 | };
155 |
156 | internal unsafe struct TypeTreeListNode
157 | {
158 | public TypeTreeListNode* Next;
159 | public TypeTreeListNode* Prev;
160 | public TypeTree Value;
161 | }
162 |
163 | internal struct TypeTree
164 | {
165 | public TypeTreeList m_Children;
166 | public TypeTree* m_Father;
167 | public TypeTreeString m_Type;
168 | public TypeTreeString m_Name;
169 | public int m_ByteSize;
170 | public int m_Index;
171 | public int m_IsArray;
172 | public int m_Version;
173 | public TransferMetaFlags m_MetaFlag;
174 | public int m_ByteOffset;
175 | public void* m_DirectPtr;
176 | }
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V5_0.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 |
6 | namespace Unity
7 | {
8 | public partial class TypeTree
9 | {
10 | // Unity 5.0 - 5.2
11 | unsafe class V5_0 : ITypeTreeImpl
12 | {
13 | internal TypeTree Tree;
14 |
15 | public IReadOnlyList StringBuffer => Tree.StringBuffer;
16 |
17 | public IReadOnlyList Nodes => m_Nodes;
18 |
19 | public IReadOnlyList ByteOffsets => Tree.ByteOffsets;
20 |
21 | private TypeTreeNode[] m_Nodes;
22 |
23 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
24 | delegate void TypeTreeDelegate(out TypeTree tree);
25 |
26 | public V5_0(ManagedTypeTree owner, SymbolResolver resolver)
27 | {
28 | TypeTree tree;
29 | var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@XZ");
30 | constructor(&tree);
31 | Tree = tree;
32 | }
33 |
34 | public ref byte GetPinnableReference()
35 | {
36 | return ref Unsafe.As(ref Tree);
37 | }
38 |
39 | public void CreateNodes(ManagedTypeTree owner)
40 | {
41 | var nodes = new TypeTreeNode[Tree.Nodes.Size];
42 |
43 | for (int i = 0; i < nodes.Length; i++)
44 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V5_0(Tree.Nodes.Ptr[i]), owner);
45 |
46 | m_Nodes = nodes;
47 | }
48 |
49 | internal struct TypeTree
50 | {
51 | public DynamicArray Nodes;
52 | public DynamicArray StringBuffer;
53 | public DynamicArray ByteOffsets;
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.V5_3.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.InteropServices;
3 | using System.Runtime.CompilerServices;
4 | using ManagedTypeTree = Unity.TypeTree;
5 |
6 | namespace Unity
7 | {
8 | public partial class TypeTree
9 | {
10 | // Unity 5.3 - 2018.4
11 | unsafe class V5_3 : ITypeTreeImpl
12 | {
13 | internal TypeTree Tree;
14 |
15 | public IReadOnlyList StringBuffer => Tree.StringBuffer;
16 |
17 | public IReadOnlyList Nodes => m_Nodes;
18 |
19 | public IReadOnlyList ByteOffsets => Tree.ByteOffsets;
20 |
21 | private TypeTreeNode[] m_Nodes;
22 |
23 | public V5_3(ManagedTypeTree owner, SymbolResolver resolver)
24 | {
25 | var constructor = (delegate* unmanaged[Cdecl])resolver.Resolve($"??0TypeTree@@Q{NameMangling.Ptr64}AA@A{NameMangling.Ptr64}BUMemLabelId@@@Z");
26 | var label = resolver.Resolve("?kMemTypeTree@@3UMemLabelId@@A");
27 | TypeTree tree;
28 | constructor(&tree, label);
29 | Tree = tree;
30 | }
31 |
32 | public ref byte GetPinnableReference()
33 | {
34 | return ref Unsafe.As(ref Tree);
35 | }
36 |
37 | public void CreateNodes(ManagedTypeTree owner)
38 | {
39 | var nodes = new TypeTreeNode[Tree.Nodes.Size];
40 |
41 | for (int i = 0; i < nodes.Length; i++)
42 | nodes[i] = new TypeTreeNode(new TypeTreeNode.V5_0(Tree.Nodes.Ptr[i]), owner);
43 |
44 | m_Nodes = nodes;
45 | }
46 |
47 | internal struct TypeTree
48 | {
49 | public DynamicArray Nodes;
50 | public DynamicArray StringBuffer;
51 | public DynamicArray ByteOffsets;
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTree.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace Unity
6 | {
7 | public unsafe partial class TypeTree
8 | {
9 | readonly CommonString strings;
10 |
11 | readonly ITypeTreeImpl tree;
12 |
13 | public TypeTree(UnityVersion version, CommonString strings, SymbolResolver resolver)
14 | {
15 | if (version < UnityVersion.Unity3_5)
16 | tree = new V3_4(this, resolver);
17 | else if (version < UnityVersion.Unity4_0)
18 | tree = new V3_5(this, resolver);
19 | else if (version < UnityVersion.Unity5_0)
20 | tree = new V4_0(this, resolver);
21 | else if (version < UnityVersion.Unity5_3)
22 | tree = new V5_0(this, resolver);
23 | else if (version < UnityVersion.Unity2019_1)
24 | tree = new V5_3(this, resolver);
25 | else if (version < UnityVersion.Unity2019_3)
26 | tree = new V2019_1(this, resolver);
27 | else if (version < UnityVersion.Unity2022_2)
28 | tree = new V2019_3(this, resolver);
29 | else if (version < UnityVersion.Unity2023_1_0a2)
30 | tree = new V2022_2(this, resolver);
31 | else
32 | tree = new V2023_1(this, resolver);
33 |
34 | this.strings = strings;
35 | }
36 |
37 | public ref byte GetPinnableReference()
38 | {
39 | return ref tree.GetPinnableReference();
40 | }
41 |
42 | public string GetString(uint offset)
43 | {
44 | if (offset > int.MaxValue)
45 | return Marshal.PtrToStringAnsi((IntPtr)(strings.BufferBegin + (int)(int.MaxValue & offset)));
46 |
47 | string str = "";
48 | for(int i = (int)offset; tree.StringBuffer[i] != 0; i++)
49 | {
50 | str += (char)tree.StringBuffer[i];
51 | }
52 | return str;
53 | }
54 |
55 | public void CreateNodes()
56 | {
57 | tree.CreateNodes(this);
58 | }
59 |
60 | public TypeTreeNode this[int index] => tree.Nodes[index];
61 |
62 | public int Count => tree.Nodes.Count;
63 |
64 | public IReadOnlyList StringBuffer => tree.StringBuffer;
65 |
66 | public IReadOnlyList ByteOffsets => tree.ByteOffsets;
67 |
68 | interface ITypeTreeImpl
69 | {
70 | IReadOnlyList StringBuffer { get; }
71 | IReadOnlyList Nodes { get; }
72 | IReadOnlyList ByteOffsets { get; }
73 | ref byte GetPinnableReference();
74 | void CreateNodes(TypeTree tree);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTreeFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Unity
5 | {
6 | public unsafe class TypeTreeFactory
7 | {
8 | readonly CommonString strings;
9 |
10 | readonly UnityVersion version;
11 |
12 | readonly SymbolResolver resolver;
13 |
14 | readonly delegate* unmanaged[Cdecl] getTypeTree;
15 |
16 | readonly delegate* unmanaged[Cdecl] generateTypeTree;
17 |
18 | bool HasGetTypeTree => version.Major >= 2019;
19 |
20 | public TypeTreeFactory(UnityVersion version, CommonString strings, SymbolResolver resolver)
21 | {
22 | this.version = version;
23 | this.resolver = resolver;
24 | this.strings = strings;
25 |
26 | if (HasGetTypeTree)
27 | getTypeTree = (delegate* unmanaged[Cdecl])resolver.Resolve($"?GetTypeTree@TypeTreeCache@@YA_NP{NameMangling.Ptr64}BVObject@@W4TransferInstructionFlags@@A{NameMangling.Ptr64}AVTypeTree@@@Z");
28 | else
29 | {
30 | generateTypeTree = (delegate* unmanaged[Cdecl])resolver.Resolve(
31 | $"?GenerateTypeTree@@YAXA{NameMangling.Ptr64}BVObject@@A{NameMangling.Ptr64}AVTypeTree@@W4TransferInstructionFlags@@@Z",
32 | $"?GenerateTypeTree@@YAXA{NameMangling.Ptr64}AVObject@@P{NameMangling.Ptr64}AVTypeTree@@W4TransferInstructionFlags@@@Z",
33 | $"?GenerateTypeTree@@YAXA{NameMangling.Ptr64}AVObject@@P{NameMangling.Ptr64}AVTypeTree@@H@Z"
34 | );
35 | }
36 | }
37 |
38 | public unsafe TypeTree GetTypeTree(NativeObject @object, TransferInstructionFlags flags)
39 | {
40 | var tree = new TypeTree(version, strings, resolver);
41 |
42 | fixed (byte* pointer = tree)
43 | {
44 | if (HasGetTypeTree)
45 | {
46 | getTypeTree(@object.Pointer, flags, pointer);
47 | }
48 | else
49 | {
50 | generateTypeTree(@object.Pointer, pointer, flags);
51 | }
52 | }
53 |
54 | tree.CreateNodes();
55 |
56 | return tree;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTreeNode.V1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace Unity
5 | {
6 | public partial class TypeTreeNode
7 | {
8 | internal unsafe class V1 : ITypeTreeNodeImpl
9 | {
10 | public short Version { get; private set; }
11 |
12 | public byte Level { get; private set; }
13 |
14 | public TypeFlags TypeFlags { get; private set; }
15 |
16 | public uint TypeStrOffset { get; private set; }
17 |
18 | public uint NameStrOffset { get; private set; }
19 |
20 | public int ByteSize { get; private set; }
21 |
22 | public int Index { get; private set; }
23 |
24 | public TransferMetaFlags MetaFlag { get; private set; }
25 |
26 | internal V1(
27 | short version,
28 | byte level,
29 | TypeFlags typeFlags,
30 | uint typeStrOffset,
31 | uint nameStrOffset,
32 | int byteSize,
33 | int index,
34 | TransferMetaFlags metaFlag)
35 | {
36 | Version = version;
37 | Level = level;
38 | TypeFlags = typeFlags;
39 | TypeStrOffset = typeStrOffset;
40 | NameStrOffset = nameStrOffset;
41 | ByteSize = byteSize;
42 | Index = index;
43 | MetaFlag = metaFlag;
44 | }
45 |
46 | public ref byte GetPinnableReference()
47 | {
48 | throw new NotImplementedException();
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTreeNode.V2019_1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace Unity
5 | {
6 | public partial class TypeTreeNode
7 | {
8 | // 2019.1+
9 | internal unsafe class V2019_1 : ITypeTreeNodeImpl
10 | {
11 | internal TypeTreeNode Node;
12 |
13 | public short Version => Node.Version;
14 |
15 | public byte Level => Node.Level;
16 |
17 | public TypeFlags TypeFlags => Node.TypeFlags;
18 |
19 | public uint TypeStrOffset => Node.TypeStrOffset;
20 |
21 | public uint NameStrOffset => Node.NameStrOffset;
22 |
23 | public int ByteSize => Node.ByteSize;
24 |
25 | public int Index => Node.Index;
26 |
27 | public TransferMetaFlags MetaFlag => Node.MetaFlag;
28 |
29 | internal V2019_1(TypeTreeNode node)
30 | {
31 | Node = node;
32 | }
33 |
34 | public V2019_1(IntPtr address)
35 | {
36 | if (address == IntPtr.Zero)
37 | throw new ArgumentNullException(nameof(address));
38 |
39 | Node = *(TypeTreeNode*)address;
40 | }
41 |
42 | public ref byte GetPinnableReference()
43 | {
44 | return ref Unsafe.As(ref Node);
45 | }
46 |
47 | internal struct TypeTreeNode
48 | {
49 | public short Version;
50 | public byte Level;
51 | public TypeFlags TypeFlags;
52 | public uint TypeStrOffset;
53 | public uint NameStrOffset;
54 | public int ByteSize;
55 | public int Index;
56 | public TransferMetaFlags MetaFlag;
57 | public ulong RefTypeHash;
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTreeNode.V5_0.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 |
5 | namespace Unity
6 | {
7 | public partial class TypeTreeNode
8 | {
9 | internal unsafe class V5_0 : ITypeTreeNodeImpl
10 | {
11 | internal TypeTreeNode Node;
12 |
13 | public short Version => Node.Version;
14 |
15 | public byte Level => Node.Level;
16 |
17 | public TypeFlags TypeFlags => Node.TypeFlags;
18 |
19 | public uint TypeStrOffset => Node.TypeStrOffset;
20 |
21 | public uint NameStrOffset => Node.NameStrOffset;
22 |
23 | public int ByteSize => Node.ByteSize;
24 |
25 | public int Index => Node.Index;
26 |
27 | public TransferMetaFlags MetaFlag => Node.MetaFlag;
28 |
29 | internal V5_0(TypeTreeNode node)
30 | {
31 | Node = node;
32 | }
33 |
34 | public V5_0(IntPtr address)
35 | {
36 | if (address == IntPtr.Zero)
37 | throw new ArgumentNullException(nameof(address));
38 |
39 | Node = *(TypeTreeNode*)address;
40 | }
41 |
42 | public ref byte GetPinnableReference()
43 | {
44 | return ref Unsafe.As(ref Node);
45 | }
46 |
47 | internal struct TypeTreeNode
48 | {
49 | public short Version;
50 | public byte Level;
51 | public TypeFlags TypeFlags;
52 | public uint TypeStrOffset;
53 | public uint NameStrOffset;
54 | public int ByteSize;
55 | public int Index;
56 | public TransferMetaFlags MetaFlag;
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/UnityInterop/TypeTree/TypeTreeNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | public unsafe partial class TypeTreeNode
6 | {
7 | readonly TypeTree owner;
8 |
9 | readonly ITypeTreeNodeImpl node;
10 |
11 |
12 | public short Version => node.Version;
13 |
14 | public byte Level => node.Level;
15 |
16 | public TypeFlags TypeFlags => node.TypeFlags;
17 |
18 | public uint TypeStrOffset => node.TypeStrOffset;
19 |
20 | public uint NameStrOffset => node.NameStrOffset;
21 |
22 | public string TypeName => owner.GetString(node.TypeStrOffset);
23 |
24 | public string Name => owner.GetString(node.NameStrOffset);
25 |
26 | public int ByteSize => node.ByteSize;
27 |
28 | public int Index => node.Index;
29 |
30 | public TransferMetaFlags MetaFlag => node.MetaFlag;
31 |
32 | internal TypeTreeNode(ITypeTreeNodeImpl impl, TypeTree owner)
33 | {
34 | this.owner = owner;
35 | node = impl;
36 | }
37 |
38 | public TypeTreeNode(UnityVersion version, TypeTree owner, IntPtr address)
39 | {
40 | this.owner = owner;
41 |
42 | if (version >= UnityVersion.Unity2019_1)
43 | node = new V2019_1(address);
44 | else
45 | node = new V5_0(address);
46 | }
47 |
48 | internal interface ITypeTreeNodeImpl
49 | {
50 | public short Version { get; }
51 | public byte Level { get; }
52 | public TypeFlags TypeFlags { get; }
53 | public uint TypeStrOffset { get; }
54 | public uint NameStrOffset { get; }
55 | public int ByteSize { get; }
56 | public int Index { get; }
57 | public TransferMetaFlags MetaFlag { get; }
58 | ref byte GetPinnableReference();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/UnityInterop/UnityEngine.cs:
--------------------------------------------------------------------------------
1 | namespace Unity
2 | {
3 | // todo: should all classes just take a UnityEngine argument?
4 | public class UnityEngine
5 | {
6 | public UnityVersion Version { get; }
7 |
8 | public CommonString CommonString { get; }
9 |
10 | public TypeTreeFactory TypeTreeFactory { get; }
11 |
12 | public RuntimeTypeArray RuntimeTypes { get; }
13 |
14 | public NativeObjectFactory ObjectFactory { get; }
15 |
16 | public JsonHandler JsonHandler { get; }
17 |
18 |
19 | public UnityEngine(UnityVersion version, SymbolResolver resolver)
20 | {
21 | Version = version;
22 | CommonString = new CommonString(resolver);
23 | RuntimeTypes = new RuntimeTypeArray(version, resolver);
24 | TypeTreeFactory = new TypeTreeFactory(version, CommonString, resolver);
25 | ObjectFactory = new NativeObjectFactory(version, resolver);
26 | JsonHandler = new JsonHandler(version, resolver);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/UnityInterop/UnityInterop.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | UnityInterop
4 | net6.0-windows
5 | latest
6 | True
7 |
8 |
9 |
--------------------------------------------------------------------------------
/UnityInterop/UnityVersion.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace Unity
5 | {
6 | public enum UnityVersionType
7 | {
8 | Alpha,
9 | Beta,
10 | Final,
11 | Patch,
12 | Experimental
13 | }
14 |
15 | public readonly struct UnityVersion : IComparable, IEquatable
16 | {
17 | public readonly int Major;
18 |
19 | public readonly int Minor;
20 |
21 | public readonly int Patch;
22 |
23 | public readonly UnityVersionType Type;
24 |
25 | public readonly int Build;
26 |
27 | public static readonly UnityVersion Unity2_5 = new UnityVersion(2, 5);
28 |
29 | public static readonly UnityVersion Unity2_6 = new UnityVersion(2, 6);
30 |
31 | public static readonly UnityVersion Unity3_0 = new UnityVersion(3, 0);
32 |
33 | public static readonly UnityVersion Unity3_1 = new UnityVersion(3, 1);
34 |
35 | public static readonly UnityVersion Unity3_2 = new UnityVersion(3, 2);
36 |
37 | public static readonly UnityVersion Unity3_3 = new UnityVersion(3, 3);
38 |
39 | public static readonly UnityVersion Unity3_4 = new UnityVersion(3, 4);
40 |
41 | public static readonly UnityVersion Unity3_5 = new UnityVersion(3, 5);
42 |
43 | public static readonly UnityVersion Unity4_0 = new UnityVersion(4, 0);
44 |
45 | public static readonly UnityVersion Unity4_1 = new UnityVersion(4, 1);
46 |
47 | public static readonly UnityVersion Unity4_2 = new UnityVersion(4, 2);
48 |
49 | public static readonly UnityVersion Unity4_3 = new UnityVersion(4, 3);
50 |
51 | public static readonly UnityVersion Unity4_4 = new UnityVersion(4, 4);
52 |
53 | public static readonly UnityVersion Unity4_5 = new UnityVersion(4, 5);
54 |
55 | public static readonly UnityVersion Unity4_6 = new UnityVersion(4, 6);
56 |
57 | public static readonly UnityVersion Unity4_7 = new UnityVersion(4, 7);
58 |
59 | public static readonly UnityVersion Unity5_0 = new UnityVersion(5, 0);
60 |
61 | public static readonly UnityVersion Unity5_1 = new UnityVersion(5, 1);
62 |
63 | public static readonly UnityVersion Unity5_2 = new UnityVersion(5, 2);
64 |
65 | public static readonly UnityVersion Unity5_3 = new UnityVersion(5, 3);
66 |
67 | public static readonly UnityVersion Unity5_4 = new UnityVersion(5, 4);
68 |
69 | public static readonly UnityVersion Unity5_5 = new UnityVersion(5, 5);
70 |
71 | public static readonly UnityVersion Unity5_6 = new UnityVersion(5, 6);
72 |
73 | public static readonly UnityVersion Unity2017_1 = new UnityVersion(2017, 1);
74 |
75 | public static readonly UnityVersion Unity2017_2 = new UnityVersion(2017, 2);
76 |
77 | public static readonly UnityVersion Unity2017_3 = new UnityVersion(2017, 3);
78 |
79 | public static readonly UnityVersion Unity2017_4 = new UnityVersion(2017, 4);
80 |
81 | public static readonly UnityVersion Unity2018_1 = new UnityVersion(2018, 1);
82 |
83 | public static readonly UnityVersion Unity2018_2 = new UnityVersion(2018, 2);
84 |
85 | public static readonly UnityVersion Unity2018_3 = new UnityVersion(2018, 3);
86 |
87 | public static readonly UnityVersion Unity2018_4 = new UnityVersion(2018, 4);
88 |
89 | public static readonly UnityVersion Unity2019_1 = new UnityVersion(2019, 1);
90 |
91 | public static readonly UnityVersion Unity2019_2 = new UnityVersion(2019, 2);
92 |
93 | public static readonly UnityVersion Unity2019_3 = new UnityVersion(2019, 3);
94 |
95 | public static readonly UnityVersion Unity2019_4 = new UnityVersion(2019, 4);
96 |
97 | public static readonly UnityVersion Unity2020_1 = new UnityVersion(2020, 1);
98 |
99 | public static readonly UnityVersion Unity2020_2 = new UnityVersion(2020, 2);
100 |
101 | public static readonly UnityVersion Unity2020_3 = new UnityVersion(2020, 3);
102 |
103 | public static readonly UnityVersion Unity2021_1 = new UnityVersion(2021, 1);
104 |
105 | public static readonly UnityVersion Unity2021_2 = new UnityVersion(2021, 2);
106 |
107 | public static readonly UnityVersion Unity2022_1 = new UnityVersion(2022, 1);
108 |
109 | public static readonly UnityVersion Unity2022_2 = new UnityVersion(2022, 2);
110 |
111 | public static readonly UnityVersion Unity2023_1 = new UnityVersion(2023, 1);
112 |
113 | public static readonly UnityVersion Unity2023_1_0a2 = new UnityVersion(2023, 1, 0, 'a', 2);
114 |
115 | public UnityVersion(int major, int minor)
116 | : this(major, minor, 0, 0, 0)
117 | {
118 | }
119 |
120 | public UnityVersion(int major, int minor, int patch)
121 | : this(major, minor, patch, 0, 0)
122 | {
123 | }
124 |
125 | public UnityVersion(int major, int minor, int patch, char type, int build)
126 | : this(major, minor, patch, VersionTypeFromChar(type), build)
127 | {
128 | }
129 |
130 | public UnityVersion(int major, int minor, int patch, UnityVersionType type, int build)
131 | {
132 | Major = major;
133 | Minor = minor;
134 | Patch = patch;
135 | Type = type;
136 | Build = build;
137 | }
138 |
139 | public UnityVersion(string version)
140 | {
141 | var match = Regex.Match(version, @"(\d+)\.(\d+)\.(\d+)([abfpx])(\d+)");
142 |
143 | if (!match.Success)
144 | throw new ArgumentException("Invalid version string.", nameof(version));
145 |
146 | Major = int.Parse(match.Groups[1].Value);
147 | Minor = int.Parse(match.Groups[2].Value);
148 | Patch = int.Parse(match.Groups[3].Value);
149 | Type = VersionTypeFromChar(match.Groups[4].Value[0]);
150 | Build = int.Parse(match.Groups[5].Value);
151 | }
152 |
153 | public int CompareTo(UnityVersion other)
154 | {
155 | if (Major != other.Major)
156 | return Major.CompareTo(other.Major);
157 |
158 | if (Minor != other.Minor)
159 | return Minor.CompareTo(other.Minor);
160 |
161 | if (Patch != other.Patch)
162 | return Patch.CompareTo(other.Patch);
163 |
164 | if (Type != other.Type)
165 | return Type.CompareTo(other.Type);
166 |
167 | return Build.CompareTo(other.Build);
168 | }
169 |
170 | public static bool operator ==(UnityVersion a, UnityVersion b) => a.CompareTo(b) == 0;
171 |
172 | public static bool operator !=(UnityVersion a, UnityVersion b) => a.CompareTo(b) != 0;
173 |
174 | public static bool operator >(UnityVersion a, UnityVersion b) => a.CompareTo(b) > 0;
175 |
176 | public static bool operator <(UnityVersion a, UnityVersion b) => a.CompareTo(b) < 0;
177 |
178 | public static bool operator >=(UnityVersion a, UnityVersion b) => a.CompareTo(b) >= 0;
179 |
180 | public static bool operator <=(UnityVersion a, UnityVersion b) => a.CompareTo(b) <= 0;
181 |
182 | public static explicit operator Version(UnityVersion version) => new Version(version.Major,version.Minor,version.Patch,version.Build);
183 |
184 | public bool Equals(UnityVersion other) => CompareTo(other) == 0;
185 |
186 | public override bool Equals(object obj) => obj is UnityVersion version && Equals(version);
187 |
188 | public override int GetHashCode() => HashCode.Combine(Major, Minor, Patch, Type, Build);
189 |
190 | public override string ToString() => string.Format("{0}.{1}.{2}{3}{4}", Major, Minor, Patch, CharFromVersionType(Type), Build);
191 |
192 | static char CharFromVersionType(UnityVersionType type) => type switch
193 | {
194 | UnityVersionType.Alpha => 'a',
195 | UnityVersionType.Beta => 'b',
196 | UnityVersionType.Final => 'f',
197 | UnityVersionType.Patch => 'p',
198 | UnityVersionType.Experimental => 'x',
199 | _ => throw new ArgumentOutOfRangeException(nameof(type))
200 | };
201 |
202 | static UnityVersionType VersionTypeFromChar(char c) => c switch
203 | {
204 | 'a' => UnityVersionType.Alpha,
205 | 'b' => UnityVersionType.Beta,
206 | 'f' => UnityVersionType.Final,
207 | 'p' => UnityVersionType.Patch,
208 | 'x' => UnityVersionType.Experimental,
209 | _ => throw new ArgumentOutOfRangeException(nameof(c))
210 | };
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/UnityInterop/UnresolvedSymbolException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity
4 | {
5 | public sealed class UnresolvedSymbolException : Exception
6 | {
7 | const string DefaultMessage = "Symbol has not been resolved.";
8 |
9 | public string SymbolName { get; }
10 |
11 | public UnresolvedSymbolException()
12 | : base(DefaultMessage)
13 | {
14 | }
15 |
16 | public UnresolvedSymbolException(Exception inner)
17 | : base(DefaultMessage, inner)
18 | {
19 | }
20 |
21 | public UnresolvedSymbolException(string symbol)
22 | : base(DefaultMessage)
23 | {
24 | SymbolName = symbol;
25 | }
26 |
27 | public UnresolvedSymbolException(string symbol, Exception inner)
28 | : base(DefaultMessage, inner)
29 | {
30 | SymbolName = symbol;
31 | }
32 |
33 | public UnresolvedSymbolException(string symbol, string message)
34 | : base(message)
35 | {
36 | SymbolName = symbol;
37 | }
38 |
39 | public UnresolvedSymbolException(string symbol, string message, Exception inner)
40 | : base(message, inner)
41 | {
42 | SymbolName = symbol;
43 | }
44 |
45 | public override string Message
46 | {
47 | get
48 | {
49 | var message = base.Message;
50 |
51 | if (!string.IsNullOrEmpty(message) && !string.IsNullOrEmpty(SymbolName))
52 | message += $" ({SymbolName})";
53 |
54 | return message;
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------