├── .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 | --------------------------------------------------------------------------------