├── .gitattributes ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── LICENSE.txt ├── Metadata.CLI ├── Metadata.CLI.csproj └── Program.cs ├── Metadata.sln ├── Metadata ├── Attributes │ ├── ArrayLengthAttribute.cs │ └── VersionAttribute.cs ├── Converters │ ├── BlocksConverter.cs │ └── StructConverter.cs ├── Crypto │ ├── AES.cs │ ├── CryptoHelper.cs │ └── MT19937_64.cs ├── Extensions │ ├── BinaryStreamExtensions.cs │ ├── MemoryStreamExtensions.cs │ ├── PEFileExtensions.cs │ └── TypeExtensions.cs ├── IL2CPP │ ├── MetadataClass.cs │ └── MhyMetadataClass.cs ├── Managers │ └── MetaManager.cs ├── MetaTypes │ ├── Blocks.cs │ ├── Mark.cs │ ├── MetaBase.cs │ ├── Struct.cs │ └── Usages.cs ├── Metadata.csproj └── Utils │ ├── BinaryStream.cs │ └── ConvertersUtils.cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | paths-ignore: 7 | - '**/README.md' 8 | - '.github/**' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | 13 | build: 14 | 15 | runs-on: windows-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | 21 | - name: Install .NET Core 22 | uses: actions/setup-dotnet@v3 23 | with: 24 | dotnet-version: 6.0.x 25 | 26 | - name: Restore the application 27 | run: nuget restore 28 | 29 | - name: Build the application 30 | run: dotnet publish /t:Metadata_CLI /p:Configuration=Release 31 | 32 | - name: Upload build artifacts 33 | uses: actions/upload-artifact@v3 34 | with: 35 | name: net6.0 36 | path: Metadata.CLI/bin/Release/net6.0/publish 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | /MetadataConverter2.CLI/Properties/launchSettings.json 365 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 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 | -------------------------------------------------------------------------------- /Metadata.CLI/Metadata.CLI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Metadata.CLI/Program.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Managers; 2 | using MetadataConverter2.MetaTypes; 3 | using System.Reflection; 4 | 5 | if (args.Length is not 3 and not 4) 6 | { 7 | Console.WriteLine(GetHelpMessage()); 8 | return; 9 | } 10 | 11 | try 12 | { 13 | bool convert = false; 14 | FileInfo inputPath = new(args[0]); 15 | FileInfo outputPath = new(args[1]); 16 | 17 | if (!Enum.TryParse(args[2], true, out MetaType gameType)) 18 | { 19 | throw new Exception($"Invalid Meta\n" + MetaManager.SupportedMetas()); 20 | } 21 | 22 | if (args.Length == 4) 23 | { 24 | convert = args[3] == "convert"; 25 | } 26 | 27 | Console.WriteLine($"Processing..."); 28 | byte[] metadataBytes = File.ReadAllBytes(inputPath.FullName); 29 | 30 | using var metadataStream = new MemoryStream(); 31 | metadataStream.Write(metadataBytes); 32 | MetaBase game = MetaManager.GetMeta(gameType); 33 | if (!game.Decrypt(metadataStream)) 34 | { 35 | Console.WriteLine("Unable to decrypt metadata !!"); 36 | return; 37 | } 38 | if (convert) 39 | { 40 | Console.WriteLine($"Converting..."); 41 | if (!game.Convert(metadataStream)) 42 | { 43 | Console.WriteLine("Unable to convert metadata !!"); 44 | return; 45 | } 46 | } 47 | 48 | Console.WriteLine($"Writing..."); 49 | File.WriteAllBytes(outputPath.FullName, metadataStream.ToArray()); 50 | 51 | Console.WriteLine("Done"); 52 | } 53 | catch (Exception e) 54 | { 55 | Console.WriteLine(e); 56 | } 57 | 58 | static string GetHelpMessage() 59 | { 60 | string VersionString = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 61 | string HelpMsg = $@"Metadata v{VersionString} 62 | ------------------------ 63 | Usage: 64 | Metadata [convert] 65 | 66 | Arguments: 67 | Input metadata file. 68 | Output metadata file. 69 | ({string.Join('|', Enum.GetNames(typeof(MetaType)))}) 70 | [convert] enable converting to standard unity format. 71 | "; 72 | return HelpMsg; 73 | } -------------------------------------------------------------------------------- /Metadata.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Metadata", "Metadata\Metadata.csproj", "{B1CBA968-2661-4A02-AF04-2AAFA1F92731}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Metadata.CLI", "Metadata.CLI\Metadata.CLI.csproj", "{CA828E8F-0A77-4161-A6EF-7EE047125CA3}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B1CBA968-2661-4A02-AF04-2AAFA1F92731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {B1CBA968-2661-4A02-AF04-2AAFA1F92731}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {B1CBA968-2661-4A02-AF04-2AAFA1F92731}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {B1CBA968-2661-4A02-AF04-2AAFA1F92731}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {CA828E8F-0A77-4161-A6EF-7EE047125CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {CA828E8F-0A77-4161-A6EF-7EE047125CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {CA828E8F-0A77-4161-A6EF-7EE047125CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {CA828E8F-0A77-4161-A6EF-7EE047125CA3}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {70E57101-4791-463F-ADF3-12459A5FFDCA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Metadata/Attributes/ArrayLengthAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.Attributes; 2 | [AttributeUsage(AttributeTargets.Field)] 3 | internal class ArrayLengthAttribute : Attribute 4 | { 5 | public int Length { get; set; } 6 | } -------------------------------------------------------------------------------- /Metadata/Attributes/VersionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.Attributes; 2 | [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] 3 | public class VersionAttribute : Attribute 4 | { 5 | public double Min { get; set; } = 0; 6 | public double Max { get; set; } = 99; 7 | } 8 | -------------------------------------------------------------------------------- /Metadata/Converters/BlocksConverter.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Extensions; 2 | using MetadataConverter2.IL2CPP; 3 | using MetadataConverter2.Utils; 4 | using static MetadataConverter2.IL2CPP.UnityIl2Cpp; 5 | 6 | namespace MetadataConverter2.Converters; 7 | public static class BlocksConverter 8 | { 9 | public static bool Convert(MemoryStream stream, double version, MetadataUsagePair[] metadataUsagePairs = null, MetadataUsageList[] metadataUsageList = null) 10 | { 11 | using BinaryStream bs = new(stream) { Version = version }; 12 | using MemoryStream ms = new(0x1000); 13 | using BinaryStream newBS = new(ms) { Version = version }; 14 | 15 | MhyIl2Cpp.GlobalMetadataHeader header = bs.ReadClass(0); 16 | GlobalMetadataHeader newHeader = new() 17 | { 18 | sanity = 0xFAB11BAF, 19 | version = (int)newBS.Version 20 | }; 21 | 22 | newBS.Position = (uint)typeof(GlobalMetadataHeader).SizeOf(newBS.Version); 23 | 24 | bs.ConvertMetadataSection(newBS, header.stringLiteralOffset, header.stringLiteralSize, out newHeader.stringLiteralOffset, out newHeader.stringLiteralSize, ConvertersUtils.StringLiteralConverter); 25 | bs.CopyMetadataSection(newBS, header.stringLiteralDataOffset, header.stringLiteralDataSize, out newHeader.stringLiteralDataOffset, out newHeader.stringLiteralDataSize); 26 | bs.CopyMetadataSection(newBS, header.stringOffset, header.stringSize, out newHeader.stringOffset, out newHeader.stringSize); 27 | bs.CopyMetadataSection(newBS, header.eventsOffset, header.eventsSize, out newHeader.eventsOffset, out newHeader.eventsSize); 28 | bs.ConvertMetadataSection(newBS, header.propertiesOffset, header.propertiesSize, out newHeader.propertiesOffset, out newHeader.propertiesSize, ConvertersUtils.PropertyDefinitionConverter); 29 | bs.ConvertMetadataSection(newBS, header.methodsOffset, header.methodsSize, out newHeader.methodsOffset, out newHeader.methodsSize, ConvertersUtils.MethodDefinitionConverter); 30 | bs.CopyMetadataSection(newBS, header.parameterDefaultValuesOffset, header.parameterDefaultValuesSize, out newHeader.parameterDefaultValuesOffset, out newHeader.parameterDefaultValuesSize); 31 | bs.CopyMetadataSection(newBS, header.fieldDefaultValuesOffset, header.fieldDefaultValuesSize, out newHeader.fieldDefaultValuesOffset, out newHeader.fieldDefaultValuesSize); 32 | bs.CopyMetadataSection(newBS, header.fieldAndParameterDefaultValueDataOffset, header.fieldAndParameterDefaultValueDataSize, out newHeader.fieldAndParameterDefaultValueDataOffset, out newHeader.fieldAndParameterDefaultValueDataSize); 33 | bs.CopyMetadataSection(newBS, (int)header.fieldMarshaledSizesOffset, header.fieldMarshaledSizesSize, out newHeader.fieldMarshaledSizesOffset, out newHeader.fieldMarshaledSizesSize); 34 | bs.CopyMetadataSection(newBS, header.parametersOffset, header.parametersSize, out newHeader.parametersOffset, out newHeader.parametersSize); 35 | bs.ConvertMetadataSection(newBS, header.fieldsOffset, header.fieldsSize, out newHeader.fieldsOffset, out newHeader.fieldsSize, ConvertersUtils.FieldDefinitionConverter); 36 | bs.CopyMetadataSection(newBS, header.genericParametersOffset, header.genericParametersSize, out newHeader.genericParametersOffset, out newHeader.genericParametersSize); 37 | bs.CopyMetadataSection(newBS, header.genericParameterConstraintsOffset, header.genericParameterConstraintsSize, out newHeader.genericParameterConstraintsOffset, out newHeader.genericParameterConstraintsSize); 38 | bs.CopyMetadataSection(newBS, header.genericContainersOffset, header.genericContainersSize, out newHeader.genericContainersOffset, out newHeader.genericContainersSize); 39 | bs.CopyMetadataSection(newBS, header.nestedTypesOffset, header.nestedTypesSize, out newHeader.nestedTypesOffset, out newHeader.nestedTypesSize); 40 | bs.CopyMetadataSection(newBS, header.interfacesOffset, header.interfacesSize, out newHeader.interfacesOffset, out newHeader.interfacesSize); 41 | bs.CopyMetadataSection(newBS, header.vtableMethodsOffset, header.vtableMethodsSize, out newHeader.vtableMethodsOffset, out newHeader.vtableMethodsSize); 42 | bs.CopyMetadataSection(newBS, header.interfaceOffsetsOffset, header.interfaceOffsetsSize, out newHeader.interfaceOffsetsOffset, out newHeader.interfaceOffsetsSize); 43 | bs.ConvertMetadataSection(newBS, header.typeDefinitionsOffset, header.typeDefinitionsSize, out newHeader.typeDefinitionsOffset, out newHeader.typeDefinitionsSize, ConvertersUtils.TypeDefinitionConverter); 44 | if (newBS.Version < 24.5) 45 | { 46 | bs.CopyMetadataSection(newBS, header.rgctxEntriesOffset, header.rgctxEntriesCount, out newHeader.rgctxEntriesOffset, out newHeader.rgctxEntriesCount); 47 | } 48 | bs.CopyMetadataSection(newBS, header.imagesOffset, header.imagesSize, out newHeader.imagesOffset, out newHeader.imagesSize); 49 | bs.CopyMetadataSection(newBS, header.assembliesOffset, header.assembliesSize, out newHeader.assembliesOffset, out newHeader.assembliesSize); 50 | if (newBS.Version < 24.5) 51 | { 52 | bs.CopyMetadataSection(newBS, header.metadataUsageListsOffset, header.metadataUsageListsCount, out newHeader.metadataUsageListsOffset, out newHeader.metadataUsageListsCount); 53 | bs.CopyMetadataSection(newBS, header.metadataUsagePairsOffset, header.metadataUsagePairsCount, out newHeader.metadataUsagePairsOffset, out newHeader.metadataUsagePairsCount); 54 | } 55 | else 56 | { 57 | newBS.WriteMetadataSection(metadataUsageList, out newHeader.metadataUsageListsOffset, out newHeader.metadataUsageListsCount); 58 | newBS.WriteMetadataSection(metadataUsagePairs, out newHeader.metadataUsagePairsOffset, out newHeader.metadataUsagePairsCount); 59 | } 60 | bs.CopyMetadataSection(newBS, header.fieldRefsOffset, header.fieldRefsSize, out newHeader.fieldRefsOffset, out newHeader.fieldRefsSize); 61 | bs.CopyMetadataSection(newBS, header.referencedAssembliesOffset, header.referencedAssembliesSize, out newHeader.referencedAssembliesOffset, out newHeader.referencedAssembliesSize); 62 | bs.CopyMetadataSection(newBS, header.attributesInfoOffset, header.attributesInfoCount, out newHeader.attributesInfoOffset, out newHeader.attributesInfoCount); 63 | bs.CopyMetadataSection(newBS, header.attributeTypesOffset, header.attributeTypesCount, out newHeader.attributeTypesOffset, out newHeader.attributeTypesCount); 64 | bs.CopyMetadataSection(newBS, header.unresolvedVirtualCallParameterTypesOffset, header.unresolvedVirtualCallParameterTypesSize, out newHeader.unresolvedVirtualCallParameterTypesOffset, out newHeader.unresolvedVirtualCallParameterTypesSize); 65 | bs.CopyMetadataSection(newBS, header.unresolvedVirtualCallParameterRangesOffset, header.unresolvedVirtualCallParameterRangesSize, out newHeader.unresolvedVirtualCallParameterRangesOffset, out newHeader.unresolvedVirtualCallParameterRangesSize); 66 | bs.CopyMetadataSection(newBS, header.windowsRuntimeTypeNamesOffset, header.windowsRuntimeTypeNamesSize, out newHeader.windowsRuntimeTypeNamesOffset, out newHeader.windowsRuntimeTypeNamesSize); 67 | bs.CopyMetadataSection(newBS, header.exportedTypeDefinitionsOffset, header.exportedTypeDefinitionsSize, out newHeader.exportedTypeDefinitionsOffset, out newHeader.exportedTypeDefinitionsSize); 68 | 69 | newBS.WriteClass(0, newHeader); 70 | 71 | ms.MoveTo(stream); 72 | return true; 73 | } 74 | } -------------------------------------------------------------------------------- /Metadata/Converters/StructConverter.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Extensions; 2 | using MetadataConverter2.IL2CPP; 3 | using MetadataConverter2.Utils; 4 | using static MetadataConverter2.IL2CPP.UnityIl2Cpp; 5 | 6 | namespace MetadataConverter2.Converters; 7 | public static class StructConverter 8 | { 9 | public static bool Convert(MemoryStream stream, double version, MetadataUsagePair[] metadataUsagePairs = null, MetadataUsageList[] metadataUsageList = null) 10 | { 11 | using BinaryStream bs = new(stream) { Version = version }; 12 | using MemoryStream ms = new(0x1000); 13 | using BinaryStream newBS = new(ms) { Version = version }; 14 | 15 | MhyIl2Cpp.GlobalMetadataHeader header = bs.ReadClass(0); 16 | GlobalMetadataHeader newHeader = new() 17 | { 18 | sanity = 0xFAB11BAF, 19 | version = (int)newBS.Version 20 | }; 21 | 22 | newBS.Position = (uint)typeof(GlobalMetadataHeader).SizeOf(newBS.Version); 23 | 24 | bs.ConvertMetadataSection(newBS, header.stringLiteralOffset, header.stringLiteralSize, out newHeader.stringLiteralOffset, out newHeader.stringLiteralSize, StringLiteralConverter); 25 | bs.CopyMetadataSection(newBS, header.stringLiteralDataOffset, header.stringLiteralDataSize, out newHeader.stringLiteralDataOffset, out newHeader.stringLiteralDataSize); 26 | bs.CopyMetadataSection(newBS, header.stringOffset, header.stringSize, out newHeader.stringOffset, out newHeader.stringSize); 27 | bs.CopyMetadataSection(newBS, header.eventsOffset, header.eventsSize, out newHeader.eventsOffset, out newHeader.eventsSize); 28 | bs.CopyMetadataSection(newBS, header.propertiesOffset, header.propertiesSize, out newHeader.propertiesOffset, out newHeader.propertiesSize); 29 | bs.CopyMetadataSection(newBS, header.methodsOffset, header.methodsSize, out newHeader.methodsOffset, out newHeader.methodsSize); 30 | bs.CopyMetadataSection(newBS, header.parameterDefaultValuesOffset, header.parameterDefaultValuesSize, out newHeader.parameterDefaultValuesOffset, out newHeader.parameterDefaultValuesSize); 31 | bs.CopyMetadataSection(newBS, header.fieldDefaultValuesOffset, header.fieldDefaultValuesSize, out newHeader.fieldDefaultValuesOffset, out newHeader.fieldDefaultValuesSize); 32 | bs.CopyMetadataSection(newBS, header.fieldAndParameterDefaultValueDataOffset, header.fieldAndParameterDefaultValueDataSize, out newHeader.fieldAndParameterDefaultValueDataOffset, out newHeader.fieldAndParameterDefaultValueDataSize); 33 | bs.CopyMetadataSection(newBS, (int)header.fieldMarshaledSizesOffset, header.fieldMarshaledSizesSize, out newHeader.fieldMarshaledSizesOffset, out newHeader.fieldMarshaledSizesSize); 34 | bs.CopyMetadataSection(newBS, header.parametersOffset, header.parametersSize, out newHeader.parametersOffset, out newHeader.parametersSize); 35 | bs.CopyMetadataSection(newBS, header.fieldsOffset, header.fieldsSize, out newHeader.fieldsOffset, out newHeader.fieldsSize); 36 | bs.CopyMetadataSection(newBS, header.genericParametersOffset, header.genericParametersSize, out newHeader.genericParametersOffset, out newHeader.genericParametersSize); 37 | bs.CopyMetadataSection(newBS, header.genericParameterConstraintsOffset, header.genericParameterConstraintsSize, out newHeader.genericParameterConstraintsOffset, out newHeader.genericParameterConstraintsSize); 38 | bs.CopyMetadataSection(newBS, header.genericContainersOffset, header.genericContainersSize, out newHeader.genericContainersOffset, out newHeader.genericContainersSize); 39 | bs.CopyMetadataSection(newBS, header.nestedTypesOffset, header.nestedTypesSize, out newHeader.nestedTypesOffset, out newHeader.nestedTypesSize); 40 | bs.CopyMetadataSection(newBS, header.interfacesOffset, header.interfacesSize, out newHeader.interfacesOffset, out newHeader.interfacesSize); 41 | bs.CopyMetadataSection(newBS, header.vtableMethodsOffset, header.vtableMethodsSize, out newHeader.vtableMethodsOffset, out newHeader.vtableMethodsSize); 42 | bs.CopyMetadataSection(newBS, header.interfaceOffsetsOffset, header.interfaceOffsetsSize, out newHeader.interfaceOffsetsOffset, out newHeader.interfaceOffsetsSize); 43 | bs.CopyMetadataSection(newBS, header.typeDefinitionsOffset, header.typeDefinitionsSize, out newHeader.typeDefinitionsOffset, out newHeader.typeDefinitionsSize); 44 | bs.CopyMetadataSection(newBS, header.imagesOffset, header.imagesSize, out newHeader.imagesOffset, out newHeader.imagesSize); 45 | bs.CopyMetadataSection(newBS, header.assembliesOffset, header.assembliesSize, out newHeader.assembliesOffset, out newHeader.assembliesSize); 46 | bs.CopyMetadataSection(newBS, header.metadataUsageListsOffset, header.metadataUsageListsCount, out newHeader.metadataUsageListsOffset, out newHeader.metadataUsageListsCount); 47 | bs.CopyMetadataSection(newBS, header.metadataUsagePairsOffset, header.metadataUsagePairsCount, out newHeader.metadataUsagePairsOffset, out newHeader.metadataUsagePairsCount); 48 | if (metadataUsagePairs == null && metadataUsageList == null) 49 | { 50 | bs.CopyMetadataSection(newBS, header.metadataUsageListsOffset, header.metadataUsageListsCount, out newHeader.metadataUsageListsOffset, out newHeader.metadataUsageListsCount); 51 | bs.CopyMetadataSection(newBS, header.metadataUsagePairsOffset, header.metadataUsagePairsCount, out newHeader.metadataUsagePairsOffset, out newHeader.metadataUsagePairsCount); 52 | } 53 | else 54 | { 55 | newBS.WriteMetadataSection(metadataUsageList, out newHeader.metadataUsageListsOffset, out newHeader.metadataUsageListsCount); 56 | newBS.WriteMetadataSection(metadataUsagePairs, out newHeader.metadataUsagePairsOffset, out newHeader.metadataUsagePairsCount); 57 | } 58 | bs.CopyMetadataSection(newBS, header.fieldRefsOffset, header.fieldRefsSize, out newHeader.fieldRefsOffset, out newHeader.fieldRefsSize); 59 | bs.CopyMetadataSection(newBS, header.referencedAssembliesOffset, header.referencedAssembliesSize, out newHeader.referencedAssembliesOffset, out newHeader.referencedAssembliesSize); 60 | bs.CopyMetadataSection(newBS, header.attributesInfoOffset, header.attributesInfoCount, out newHeader.attributesInfoOffset, out newHeader.attributesInfoCount); 61 | bs.CopyMetadataSection(newBS, header.attributeTypesOffset, header.attributeTypesCount, out newHeader.attributeTypesOffset, out newHeader.attributeTypesCount); 62 | bs.CopyMetadataSection(newBS, header.unresolvedVirtualCallParameterTypesOffset, header.unresolvedVirtualCallParameterTypesSize, out newHeader.unresolvedVirtualCallParameterTypesOffset, out newHeader.unresolvedVirtualCallParameterTypesSize); 63 | bs.CopyMetadataSection(newBS, header.unresolvedVirtualCallParameterRangesOffset, header.unresolvedVirtualCallParameterRangesSize, out newHeader.unresolvedVirtualCallParameterRangesOffset, out newHeader.unresolvedVirtualCallParameterRangesSize); 64 | bs.CopyMetadataSection(newBS, header.windowsRuntimeTypeNamesOffset, header.windowsRuntimeTypeNamesSize, out newHeader.windowsRuntimeTypeNamesOffset, out newHeader.windowsRuntimeTypeNamesSize); 65 | bs.CopyMetadataSection(newBS, header.exportedTypeDefinitionsOffset, header.exportedTypeDefinitionsSize, out newHeader.exportedTypeDefinitionsOffset, out newHeader.exportedTypeDefinitionsSize); 66 | 67 | newBS.WriteClass(0, newHeader); 68 | 69 | ms.MoveTo(stream); 70 | return true; 71 | } 72 | 73 | public static StringLiteral StringLiteralConverter(MhyIl2Cpp.StringLiteral value) 74 | { 75 | return new() 76 | { 77 | length = value.length, 78 | dataIndex = value.dataIndex 79 | }; 80 | } 81 | } -------------------------------------------------------------------------------- /Metadata/Crypto/AES.cs: -------------------------------------------------------------------------------- 1 | // Simple, thoroughly commented implementation of 128-bit AES / Rijndael using C# 2 | // Chris Hulbert - chris.hulbert@gmail.com - http://splinter.com.au/blog - http://github.com/chrishulbert/crypto 3 | using System.Security.Cryptography; 4 | 5 | namespace MetadataConverter2.Crypto; 6 | public static class AES 7 | { 8 | private static readonly byte[] ShiftRowsTableInv = { 0x00, 0x0D, 0x0A, 0x07, 0x04, 0x01, 0x0E, 0x0B, 0x08, 0x05, 0x02, 0x0F, 0x0C, 0x09, 0x06, 0x03 }; 9 | private static readonly byte[] LookupSBoxInv = { 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D }; 10 | private static readonly byte[] LookupG9 = { 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 0x90, 0x99, 0x82, 0x8B, 0xB4, 0xBD, 0xA6, 0xAF, 0xD8, 0xD1, 0xCA, 0xC3, 0xFC, 0xF5, 0xEE, 0xE7, 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C, 0xAB, 0xA2, 0xB9, 0xB0, 0x8F, 0x86, 0x9D, 0x94, 0xE3, 0xEA, 0xF1, 0xF8, 0xC7, 0xCE, 0xD5, 0xDC, 0x76, 0x7F, 0x64, 0x6D, 0x52, 0x5B, 0x40, 0x49, 0x3E, 0x37, 0x2C, 0x25, 0x1A, 0x13, 0x08, 0x01, 0xE6, 0xEF, 0xF4, 0xFD, 0xC2, 0xCB, 0xD0, 0xD9, 0xAE, 0xA7, 0xBC, 0xB5, 0x8A, 0x83, 0x98, 0x91, 0x4D, 0x44, 0x5F, 0x56, 0x69, 0x60, 0x7B, 0x72, 0x05, 0x0C, 0x17, 0x1E, 0x21, 0x28, 0x33, 0x3A, 0xDD, 0xD4, 0xCF, 0xC6, 0xF9, 0xF0, 0xEB, 0xE2, 0x95, 0x9C, 0x87, 0x8E, 0xB1, 0xB8, 0xA3, 0xAA, 0xEC, 0xE5, 0xFE, 0xF7, 0xC8, 0xC1, 0xDA, 0xD3, 0xA4, 0xAD, 0xB6, 0xBF, 0x80, 0x89, 0x92, 0x9B, 0x7C, 0x75, 0x6E, 0x67, 0x58, 0x51, 0x4A, 0x43, 0x34, 0x3D, 0x26, 0x2F, 0x10, 0x19, 0x02, 0x0B, 0xD7, 0xDE, 0xC5, 0xCC, 0xF3, 0xFA, 0xE1, 0xE8, 0x9F, 0x96, 0x8D, 0x84, 0xBB, 0xB2, 0xA9, 0xA0, 0x47, 0x4E, 0x55, 0x5C, 0x63, 0x6A, 0x71, 0x78, 0x0F, 0x06, 0x1D, 0x14, 0x2B, 0x22, 0x39, 0x30, 0x9A, 0x93, 0x88, 0x81, 0xBE, 0xB7, 0xAC, 0xA5, 0xD2, 0xDB, 0xC0, 0xC9, 0xF6, 0xFF, 0xE4, 0xED, 0x0A, 0x03, 0x18, 0x11, 0x2E, 0x27, 0x3C, 0x35, 0x42, 0x4B, 0x50, 0x59, 0x66, 0x6F, 0x74, 0x7D, 0xA1, 0xA8, 0xB3, 0xBA, 0x85, 0x8C, 0x97, 0x9E, 0xE9, 0xE0, 0xFB, 0xF2, 0xCD, 0xC4, 0xDF, 0xD6, 0x31, 0x38, 0x23, 0x2A, 0x15, 0x1C, 0x07, 0x0E, 0x79, 0x70, 0x6B, 0x62, 0x5D, 0x54, 0x4F, 0x46 }; 11 | private static readonly byte[] LookupG11 = { 0x00, 0x0B, 0x16, 0x1D, 0x2C, 0x27, 0x3A, 0x31, 0x58, 0x53, 0x4E, 0x45, 0x74, 0x7F, 0x62, 0x69, 0xB0, 0xBB, 0xA6, 0xAD, 0x9C, 0x97, 0x8A, 0x81, 0xE8, 0xE3, 0xFE, 0xF5, 0xC4, 0xCF, 0xD2, 0xD9, 0x7B, 0x70, 0x6D, 0x66, 0x57, 0x5C, 0x41, 0x4A, 0x23, 0x28, 0x35, 0x3E, 0x0F, 0x04, 0x19, 0x12, 0xCB, 0xC0, 0xDD, 0xD6, 0xE7, 0xEC, 0xF1, 0xFA, 0x93, 0x98, 0x85, 0x8E, 0xBF, 0xB4, 0xA9, 0xA2, 0xF6, 0xFD, 0xE0, 0xEB, 0xDA, 0xD1, 0xCC, 0xC7, 0xAE, 0xA5, 0xB8, 0xB3, 0x82, 0x89, 0x94, 0x9F, 0x46, 0x4D, 0x50, 0x5B, 0x6A, 0x61, 0x7C, 0x77, 0x1E, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2F, 0x8D, 0x86, 0x9B, 0x90, 0xA1, 0xAA, 0xB7, 0xBC, 0xD5, 0xDE, 0xC3, 0xC8, 0xF9, 0xF2, 0xEF, 0xE4, 0x3D, 0x36, 0x2B, 0x20, 0x11, 0x1A, 0x07, 0x0C, 0x65, 0x6E, 0x73, 0x78, 0x49, 0x42, 0x5F, 0x54, 0xF7, 0xFC, 0xE1, 0xEA, 0xDB, 0xD0, 0xCD, 0xC6, 0xAF, 0xA4, 0xB9, 0xB2, 0x83, 0x88, 0x95, 0x9E, 0x47, 0x4C, 0x51, 0x5A, 0x6B, 0x60, 0x7D, 0x76, 0x1F, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2E, 0x8C, 0x87, 0x9A, 0x91, 0xA0, 0xAB, 0xB6, 0xBD, 0xD4, 0xDF, 0xC2, 0xC9, 0xF8, 0xF3, 0xEE, 0xE5, 0x3C, 0x37, 0x2A, 0x21, 0x10, 0x1B, 0x06, 0x0D, 0x64, 0x6F, 0x72, 0x79, 0x48, 0x43, 0x5E, 0x55, 0x01, 0x0A, 0x17, 0x1C, 0x2D, 0x26, 0x3B, 0x30, 0x59, 0x52, 0x4F, 0x44, 0x75, 0x7E, 0x63, 0x68, 0xB1, 0xBA, 0xA7, 0xAC, 0x9D, 0x96, 0x8B, 0x80, 0xE9, 0xE2, 0xFF, 0xF4, 0xC5, 0xCE, 0xD3, 0xD8, 0x7A, 0x71, 0x6C, 0x67, 0x56, 0x5D, 0x40, 0x4B, 0x22, 0x29, 0x34, 0x3F, 0x0E, 0x05, 0x18, 0x13, 0xCA, 0xC1, 0xDC, 0xD7, 0xE6, 0xED, 0xF0, 0xFB, 0x92, 0x99, 0x84, 0x8F, 0xBE, 0xB5, 0xA8, 0xA3 }; 12 | private static readonly byte[] LookupG13 = { 0x00, 0x0D, 0x1A, 0x17, 0x34, 0x39, 0x2E, 0x23, 0x68, 0x65, 0x72, 0x7F, 0x5C, 0x51, 0x46, 0x4B, 0xD0, 0xDD, 0xCA, 0xC7, 0xE4, 0xE9, 0xFE, 0xF3, 0xB8, 0xB5, 0xA2, 0xAF, 0x8C, 0x81, 0x96, 0x9B, 0xBB, 0xB6, 0xA1, 0xAC, 0x8F, 0x82, 0x95, 0x98, 0xD3, 0xDE, 0xC9, 0xC4, 0xE7, 0xEA, 0xFD, 0xF0, 0x6B, 0x66, 0x71, 0x7C, 0x5F, 0x52, 0x45, 0x48, 0x03, 0x0E, 0x19, 0x14, 0x37, 0x3A, 0x2D, 0x20, 0x6D, 0x60, 0x77, 0x7A, 0x59, 0x54, 0x43, 0x4E, 0x05, 0x08, 0x1F, 0x12, 0x31, 0x3C, 0x2B, 0x26, 0xBD, 0xB0, 0xA7, 0xAA, 0x89, 0x84, 0x93, 0x9E, 0xD5, 0xD8, 0xCF, 0xC2, 0xE1, 0xEC, 0xFB, 0xF6, 0xD6, 0xDB, 0xCC, 0xC1, 0xE2, 0xEF, 0xF8, 0xF5, 0xBE, 0xB3, 0xA4, 0xA9, 0x8A, 0x87, 0x90, 0x9D, 0x06, 0x0B, 0x1C, 0x11, 0x32, 0x3F, 0x28, 0x25, 0x6E, 0x63, 0x74, 0x79, 0x5A, 0x57, 0x40, 0x4D, 0xDA, 0xD7, 0xC0, 0xCD, 0xEE, 0xE3, 0xF4, 0xF9, 0xB2, 0xBF, 0xA8, 0xA5, 0x86, 0x8B, 0x9C, 0x91, 0x0A, 0x07, 0x10, 0x1D, 0x3E, 0x33, 0x24, 0x29, 0x62, 0x6F, 0x78, 0x75, 0x56, 0x5B, 0x4C, 0x41, 0x61, 0x6C, 0x7B, 0x76, 0x55, 0x58, 0x4F, 0x42, 0x09, 0x04, 0x13, 0x1E, 0x3D, 0x30, 0x27, 0x2A, 0xB1, 0xBC, 0xAB, 0xA6, 0x85, 0x88, 0x9F, 0x92, 0xD9, 0xD4, 0xC3, 0xCE, 0xED, 0xE0, 0xF7, 0xFA, 0xB7, 0xBA, 0xAD, 0xA0, 0x83, 0x8E, 0x99, 0x94, 0xDF, 0xD2, 0xC5, 0xC8, 0xEB, 0xE6, 0xF1, 0xFC, 0x67, 0x6A, 0x7D, 0x70, 0x53, 0x5E, 0x49, 0x44, 0x0F, 0x02, 0x15, 0x18, 0x3B, 0x36, 0x21, 0x2C, 0x0C, 0x01, 0x16, 0x1B, 0x38, 0x35, 0x22, 0x2F, 0x64, 0x69, 0x7E, 0x73, 0x50, 0x5D, 0x4A, 0x47, 0xDC, 0xD1, 0xC6, 0xCB, 0xE8, 0xE5, 0xF2, 0xFF, 0xB4, 0xB9, 0xAE, 0xA3, 0x80, 0x8D, 0x9A, 0x97 }; 13 | private static readonly byte[] LookupG14 = { 0x00, 0x0E, 0x1C, 0x12, 0x38, 0x36, 0x24, 0x2A, 0x70, 0x7E, 0x6C, 0x62, 0x48, 0x46, 0x54, 0x5A, 0xE0, 0xEE, 0xFC, 0xF2, 0xD8, 0xD6, 0xC4, 0xCA, 0x90, 0x9E, 0x8C, 0x82, 0xA8, 0xA6, 0xB4, 0xBA, 0xDB, 0xD5, 0xC7, 0xC9, 0xE3, 0xED, 0xFF, 0xF1, 0xAB, 0xA5, 0xB7, 0xB9, 0x93, 0x9D, 0x8F, 0x81, 0x3B, 0x35, 0x27, 0x29, 0x03, 0x0D, 0x1F, 0x11, 0x4B, 0x45, 0x57, 0x59, 0x73, 0x7D, 0x6F, 0x61, 0xAD, 0xA3, 0xB1, 0xBF, 0x95, 0x9B, 0x89, 0x87, 0xDD, 0xD3, 0xC1, 0xCF, 0xE5, 0xEB, 0xF9, 0xF7, 0x4D, 0x43, 0x51, 0x5F, 0x75, 0x7B, 0x69, 0x67, 0x3D, 0x33, 0x21, 0x2F, 0x05, 0x0B, 0x19, 0x17, 0x76, 0x78, 0x6A, 0x64, 0x4E, 0x40, 0x52, 0x5C, 0x06, 0x08, 0x1A, 0x14, 0x3E, 0x30, 0x22, 0x2C, 0x96, 0x98, 0x8A, 0x84, 0xAE, 0xA0, 0xB2, 0xBC, 0xE6, 0xE8, 0xFA, 0xF4, 0xDE, 0xD0, 0xC2, 0xCC, 0x41, 0x4F, 0x5D, 0x53, 0x79, 0x77, 0x65, 0x6B, 0x31, 0x3F, 0x2D, 0x23, 0x09, 0x07, 0x15, 0x1B, 0xA1, 0xAF, 0xBD, 0xB3, 0x99, 0x97, 0x85, 0x8B, 0xD1, 0xDF, 0xCD, 0xC3, 0xE9, 0xE7, 0xF5, 0xFB, 0x9A, 0x94, 0x86, 0x88, 0xA2, 0xAC, 0xBE, 0xB0, 0xEA, 0xE4, 0xF6, 0xF8, 0xD2, 0xDC, 0xCE, 0xC0, 0x7A, 0x74, 0x66, 0x68, 0x42, 0x4C, 0x5E, 0x50, 0x0A, 0x04, 0x16, 0x18, 0x32, 0x3C, 0x2E, 0x20, 0xEC, 0xE2, 0xF0, 0xFE, 0xD4, 0xDA, 0xC8, 0xC6, 0x9C, 0x92, 0x80, 0x8E, 0xA4, 0xAA, 0xB8, 0xB6, 0x0C, 0x02, 0x10, 0x1E, 0x34, 0x3A, 0x28, 0x26, 0x7C, 0x72, 0x60, 0x6E, 0x44, 0x4A, 0x58, 0x56, 0x37, 0x39, 0x2B, 0x25, 0x0F, 0x01, 0x13, 0x1D, 0x47, 0x49, 0x5B, 0x55, 0x7F, 0x71, 0x63, 0x6D, 0xD7, 0xD9, 0xCB, 0xC5, 0xEF, 0xE1, 0xF3, 0xFD, 0xA7, 0xA9, 0xBB, 0xB5, 0x9F, 0x91, 0x83, 0x8D }; 14 | 15 | private static void SubBytesInv(byte[] a) 16 | { 17 | for (int i = 0; i < a.Length; i++) 18 | { 19 | a[i] = LookupSBoxInv[a[i]]; 20 | } 21 | } 22 | 23 | private static void XorRoundKey(byte[] state, byte[] keys, int round) 24 | { 25 | for (int i = 0; i < 0x10; i++) 26 | { 27 | state[i] ^= keys[i + (round * 0x10)]; 28 | } 29 | } 30 | 31 | private static void ShiftRowsInv(byte[] state) 32 | { 33 | byte[] temp = new byte[0x10]; 34 | Array.Copy(state, temp, 0x10); 35 | for (int i = 0; i < 0x10; i++) 36 | { 37 | state[i] = temp[ShiftRowsTableInv[i]]; 38 | } 39 | } 40 | 41 | private static void MixColInv(byte[] state, int off) 42 | { 43 | byte a0 = state[off + 0]; 44 | byte a1 = state[off + 1]; 45 | byte a2 = state[off + 2]; 46 | byte a3 = state[off + 3]; 47 | state[off + 0] = (byte)(LookupG14[a0] ^ LookupG9[a3] ^ LookupG13[a2] ^ LookupG11[a1]); 48 | state[off + 1] = (byte)(LookupG14[a1] ^ LookupG9[a0] ^ LookupG13[a3] ^ LookupG11[a2]); 49 | state[off + 2] = (byte)(LookupG14[a2] ^ LookupG9[a1] ^ LookupG13[a0] ^ LookupG11[a3]); 50 | state[off + 3] = (byte)(LookupG14[a3] ^ LookupG9[a2] ^ LookupG13[a1] ^ LookupG11[a0]); 51 | } 52 | 53 | private static void MixColsInv(byte[] state) 54 | { 55 | MixColInv(state, 0x00); 56 | MixColInv(state, 0x04); 57 | MixColInv(state, 0x08); 58 | MixColInv(state, 0x0C); 59 | } 60 | public static void Decrypt(byte[] m, byte[] keys) 61 | { 62 | byte[] c = new byte[0x10]; 63 | Array.Copy(m, c, 0x10); 64 | XorRoundKey(c, keys, 0); 65 | 66 | for (int i = 0; i < 9; i++) 67 | { 68 | SubBytesInv(c); 69 | ShiftRowsInv(c); 70 | MixColsInv(c); 71 | XorRoundKey(c, keys, i + 1); 72 | } 73 | 74 | SubBytesInv(c); 75 | ShiftRowsInv(c); 76 | XorRoundKey(c, keys, 0xA); 77 | Array.Copy(c, m, 0x10); 78 | } 79 | } 80 | 81 | public class AES128_CBC : ICryptoTransform 82 | { 83 | private readonly byte[] _iv = new byte[0x10]; 84 | private readonly byte[] _vector = new byte[0x10]; 85 | 86 | public bool CanReuseTransform => false; 87 | public bool CanTransformMultipleBlocks => false; 88 | public int InputBlockSize => 0x40; 89 | public int OutputBlockSize => 0x40; 90 | 91 | public static byte[] ExpansionKey { get; set; } = new byte[0xB0]; 92 | public AES128_CBC(byte[] iv) 93 | { 94 | iv.CopyTo(_iv, 0); 95 | iv.CopyTo(_vector, 0); 96 | } 97 | public static AES128_CBC CreateDecryptor(byte[] iv) 98 | { 99 | return new AES128_CBC(iv); 100 | } 101 | 102 | public void Reset() 103 | { 104 | _iv.CopyTo(_vector, 0); 105 | } 106 | 107 | public void Dispose() { } 108 | public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) 109 | { 110 | byte[] buffer = new byte[0x10]; 111 | byte[] previous = new byte[0x10]; 112 | for (int i = 0; i < inputCount; i += 0x10) 113 | { 114 | _vector.CopyTo(previous, 0); 115 | inputBuffer.AsSpan(inputOffset + i, 0x10).CopyTo(buffer); 116 | buffer.CopyTo(_vector, 0); 117 | AES.Decrypt(buffer, ExpansionKey); 118 | for (int j = 0; j < buffer.Length; j++) 119 | { 120 | buffer[j] ^= previous[j]; 121 | } 122 | buffer.CopyTo(outputBuffer.AsSpan(outputOffset + i, 0x10)); 123 | } 124 | 125 | return inputCount; 126 | } 127 | public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) 128 | { 129 | byte[] transformed = new byte[inputCount]; 130 | _ = TransformBlock(inputBuffer, inputOffset, inputCount, transformed, 0); 131 | return transformed; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Metadata/Crypto/CryptoHelper.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.Crypto; 2 | public static class CryptoHelper 3 | { 4 | public static readonly byte[] GIInitVector = new byte[] { 0xAD, 0x2F, 0x42, 0x30, 0x67, 0x04, 0xB0, 0x9C, 0x9D, 0x2A, 0xC0, 0xBA, 0x0E, 0xBF, 0xA5, 0x68 }; 5 | public static readonly byte[] BH3InitVector = new byte[] { 0x3F, 0x73, 0xA8, 0x5A, 0x08, 0x32, 0x0A, 0x33, 0x3C, 0xFA, 0x8D, 0x4E, 0x8B, 0x0C, 0x77, 0x45 }; 6 | public static readonly byte[] SRInitVector = new byte[] { 0xF4, 0xB9, 0x54, 0x50, 0x85, 0x21, 0xB4, 0x14, 0x6C, 0x2F, 0xF1, 0xC2, 0x88, 0x9C, 0x79, 0xC4 }; 7 | 8 | public static readonly byte[] MarkKey = new byte[] { 0x71, 0x98, 0xAA, 0xE6, 0xCE, 0x1B, 0x05, 0x4A, 0xE9, 0xFF, 0x45, 0x21, 0xC3, 0x38, 0x5E, 0x3C, 0x0F, 0xFB, 0xF5, 0xBB, 0xF6, 0x81, 0x48, 0x15, 0xFA, 0xD7, 0x77, 0x35, 0x82, 0x17, 0xD9, 0x9D, 0x56, 0x28, 0x2E, 0xA9, 0x51, 0xBA, 0x66, 0x2F, 0x22, 0xDD, 0xBB, 0x8A, 0x3B, 0xAD, 0x90, 0x63, 0xC6, 0x64, 0xFB, 0xD6, 0xCF, 0xA8, 0xBC, 0x48, 0x02, 0xC3, 0xBE, 0x36, 0xB2, 0x93, 0xBC, 0xD9 }; 9 | } -------------------------------------------------------------------------------- /Metadata/Crypto/MT19937_64.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.Crypto; 2 | public class MT19937_64 3 | { 4 | private const ulong N = 312; 5 | private const ulong M = 156; 6 | private const ulong MATRIX_A = 0xB5026F5AA96619E9L; 7 | private const ulong UPPER_MASK = 0xFFFFFFFF80000000; 8 | private const ulong LOWER_MASK = 0X7FFFFFFFUL; 9 | 10 | private readonly ulong[] mt = new ulong[N + 1]; 11 | private ulong mti = N + 1; 12 | 13 | public MT19937_64(ulong seed) 14 | { 15 | Init(seed); 16 | } 17 | 18 | public void Init(ulong seed) 19 | { 20 | mt[0] = seed; 21 | for (mti = 1; mti < N; mti++) 22 | { 23 | mt[mti] = (6364136223846793005L * (mt[mti - 1] ^ (mt[mti - 1] >> 62))) + mti; 24 | } 25 | } 26 | 27 | public ulong Int64() 28 | { 29 | ulong x; 30 | ulong[] mag01 = new ulong[2] { 0x0UL, MATRIX_A }; 31 | 32 | if (mti >= N) 33 | { 34 | ulong kk; 35 | if (mti == N + 1) 36 | { 37 | Init(5489UL); 38 | } 39 | for (kk = 0; kk < (N - M); kk++) 40 | { 41 | x = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 42 | mt[kk] = mt[kk + M] ^ (x >> 1) ^ mag01[x & 0x1UL]; 43 | } 44 | for (; kk < N - 1; kk++) 45 | { 46 | x = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 47 | mt[kk] = mt[kk - M] ^ (x >> 1) ^ mag01[x & 0x1UL]; 48 | } 49 | x = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); 50 | mt[N - 1] = mt[M - 1] ^ (x >> 1) ^ mag01[x & 0x1UL]; 51 | 52 | mti = 0; 53 | } 54 | 55 | x = mt[mti++]; 56 | x ^= (x >> 29) & 0x5555555555555555L; 57 | x ^= (x << 17) & 0x71D67FFFEDA60000L; 58 | x ^= (x << 37) & 0xFFF7EEE000000000L; 59 | x ^= x >> 43; 60 | return x; 61 | } 62 | 63 | public long Int63() 64 | { 65 | return (long)(Int64() >> 1); 66 | } 67 | 68 | public ulong IntN(ulong value) 69 | { 70 | return (ulong)Int63() % value; 71 | } 72 | } -------------------------------------------------------------------------------- /Metadata/Extensions/BinaryStreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Utils; 2 | 3 | namespace MetadataConverter2.Extensions; 4 | public static class BinaryStreamExtensions 5 | { 6 | private const int BufferSize = 81920; 7 | 8 | public static void BlockCopyTo(this BinaryStream source, BinaryStream destination, int size) 9 | { 10 | byte[] buffer = new byte[BufferSize]; 11 | for (int left = size; left > 0; left -= BufferSize) 12 | { 13 | int toRead = BufferSize < left ? BufferSize : left; 14 | int read = source.Read(buffer, 0, toRead); 15 | destination.Write(buffer, 0, read); 16 | if (read != toRead) 17 | { 18 | return; 19 | } 20 | } 21 | } 22 | public static T[] ReadMetadataClassArray(this BinaryStream stream, uint addr, int count) where T : new() 23 | { 24 | return stream.ReadClassArray(addr, count / typeof(T).SizeOf(stream.Version)); 25 | } 26 | public static void WriteMetadataClassArray(this BinaryStream stream, uint addr, int count, T[] values) where T : new() 27 | { 28 | stream.WriteClassArray(addr, count / typeof(T).SizeOf(stream.Version), values); 29 | } 30 | public static void WriteMetadataSection(this BinaryStream stream, T[] values, out uint newAddr, out int newCount) where T : new() 31 | { 32 | newAddr = (uint)stream.Position; 33 | newCount = values.Length * typeof(T).SizeOf(stream.Version); 34 | stream.WriteMetadataClassArray(newAddr, newCount, values); 35 | } 36 | public static void ConvertMetadataSection(this BinaryStream stream, BinaryStream outStream, uint addr, int count, out uint newAddr, out int newCount, Func converter) where TFrom : new() where TTo : new() 37 | { 38 | TFrom[] values = stream.ReadMetadataClassArray(addr, count); 39 | 40 | TTo[] newValues = new TTo[values.Length]; 41 | for (int i = 0; i < values.Length; i++) 42 | { 43 | newValues[i] = converter(values[i]); 44 | } 45 | 46 | outStream.WriteMetadataSection(newValues, out newAddr, out newCount); 47 | } 48 | public static void CopyMetadataSection(this BinaryStream stream, BinaryStream outStream, int addr, int count, out int newAddr, out int newCount) 49 | { 50 | CopyMetadataSection(stream, outStream, (uint)addr, count, out uint temp, out newCount); 51 | newAddr = (int)temp; 52 | } 53 | public static void CopyMetadataSection(this BinaryStream stream, BinaryStream outStream, uint addr, int count, out uint newAddr, out int newCount) 54 | { 55 | newCount = count; 56 | newAddr = (uint)outStream.Position; 57 | 58 | stream.Position = addr; 59 | stream.BlockCopyTo(outStream, count); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Metadata/Extensions/MemoryStreamExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.Extensions; 2 | public static class MemoryStreamExtensions 3 | { 4 | public static void MoveTo(this MemoryStream source, MemoryStream destination) 5 | { 6 | source.Position = 0; 7 | destination.Position = 0; 8 | source.CopyTo(destination); 9 | destination.SetLength(source.Length); 10 | } 11 | } -------------------------------------------------------------------------------- /Metadata/Extensions/PEFileExtensions.cs: -------------------------------------------------------------------------------- 1 | using AsmResolver; 2 | using AsmResolver.PE.File; 3 | using AsmResolver.PE.File.Headers; 4 | using Iced.Intel; 5 | using MetadataConverter2.Utils; 6 | using Reloaded.Memory.Sigscan; 7 | using System.Runtime.InteropServices; 8 | using static MetadataConverter2.IL2CPP.MhyIl2Cpp; 9 | using static MetadataConverter2.IL2CPP.UnityIl2Cpp; 10 | 11 | namespace MetadataConverter2.Extensions; 12 | public static class PEFileExtensions 13 | { 14 | private const string Signature = "4C 8D 0D ?? ?? ?? ?? 4C 8D 05 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? E9 ?? ?? ?? ??"; 15 | public static ulong GetVA(this PEFile peFile, ulong rva) 16 | { 17 | return peFile.OptionalHeader.ImageBase + rva; 18 | } 19 | 20 | public static bool TryGetCodegenRegisteration(this PEFile peFile, double version, out CodegenRegistration codegen) 21 | { 22 | codegen = new CodegenRegistration(); 23 | 24 | PESection? section = peFile.Sections.FirstOrDefault(x => x.Name == ".text"); 25 | if (section != default) 26 | { 27 | byte[] bytes = section.WriteIntoArray(); 28 | 29 | Scanner scanner = new(bytes); 30 | Reloaded.Memory.Sigscan.Definitions.Structs.PatternScanResult result = scanner.FindPattern(Signature); 31 | 32 | if (result.Found) 33 | { 34 | codegen.Rva = peFile.FileOffsetToRva((uint)result.Offset + section.Offset); 35 | List instructions = Decode(bytes, result.Offset, 0x1C, codegen.Rva); 36 | if (instructions.Count == 4) 37 | { 38 | codegen.UsagesRVA = instructions[^3].MemoryDisplacement32; 39 | codegen.MetadataRVA = instructions[^2].MemoryDisplacement32; 40 | 41 | codegen.Usages = peFile.ReadClass(version, codegen.UsagesRVA); 42 | codegen.Metadata = peFile.ReadClass(version, codegen.MetadataRVA); 43 | 44 | return true; 45 | } 46 | } 47 | } 48 | 49 | return false; 50 | } 51 | 52 | public static T ReadClass(this PEFile peFile, double version, uint rva) where T : new() 53 | { 54 | PESection section = peFile.GetSectionContainingRva(rva); 55 | byte[] bytes = section.WriteIntoArray(); 56 | 57 | using MemoryStream ms = new(bytes); 58 | using BinaryStream bs = new(ms) { Version = version }; 59 | return bs.ReadClass(rva - section.Rva); 60 | } 61 | 62 | public static List Decode(byte[] bytes, int offset, int count, ulong ip) 63 | { 64 | List instructions = new(); 65 | ByteArrayCodeReader codeReader = new(bytes, offset, count); 66 | Decoder decoder = Decoder.Create(64, codeReader, ip); 67 | 68 | while (codeReader.CanReadByte) 69 | { 70 | instructions.Add(decoder.Decode()); 71 | } 72 | 73 | return instructions; 74 | } 75 | } 76 | 77 | public record CodegenRegistration 78 | { 79 | public ulong Rva; 80 | public uint UsagesRVA; 81 | public uint MetadataRVA; 82 | public MhyUsages? Usages; 83 | public MetadataRegistration? Metadata; 84 | 85 | public void Patch(PEFile peFile, double version, ulong[] metadataUsages) 86 | { 87 | byte[] metadataUsagesBytes = MemoryMarshal.AsBytes(metadataUsages).ToArray(); 88 | PESection usagesSection = new(".usages", SectionFlags.ContentInitializedData | SectionFlags.MemoryRead, new DataSegment(metadataUsagesBytes)); 89 | peFile.Sections.Add(usagesSection); 90 | peFile.UpdateHeaders(); 91 | 92 | Metadata.metadataUsages = peFile.GetVA(usagesSection.Rva); 93 | Metadata.metadataUsagesCount = (ulong)metadataUsages.Length; 94 | 95 | using MemoryStream ms = new(); 96 | using BinaryStream bs = new(ms) { Version = version }; 97 | bs.WriteClass(Metadata); 98 | byte[] metadataBytes = ms.ToArray(); 99 | 100 | PESection metadataSection = peFile.GetSectionContainingRva(MetadataRVA); 101 | uint relativeOffset = (uint)(peFile.RvaToFileOffset(MetadataRVA) - metadataSection.Offset); 102 | metadataSection.Contents = metadataSection.Contents.AsPatchedSegment().Patch(relativeOffset, metadataBytes); 103 | } 104 | } -------------------------------------------------------------------------------- /Metadata/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Attributes; 2 | using System.Reflection; 3 | 4 | namespace MetadataConverter2.Extensions; 5 | public static class TypeExtensions 6 | { 7 | public static int SizeOf(this Type type, double version) 8 | { 9 | int size = 0; 10 | foreach (FieldInfo i in type.GetFields()) 11 | { 12 | VersionAttribute? attr = (VersionAttribute)Attribute.GetCustomAttribute(i, typeof(VersionAttribute)); 13 | if (attr != null) 14 | { 15 | if (version < attr.Min || version > attr.Max) 16 | { 17 | continue; 18 | } 19 | } 20 | Type fieldType = i.FieldType; 21 | if (fieldType.IsPrimitive) 22 | { 23 | size += GetPrimitiveTypeSize(fieldType.Name); 24 | } 25 | else if (fieldType.IsEnum) 26 | { 27 | Type e = fieldType.GetField("value__").FieldType; 28 | size += GetPrimitiveTypeSize(e.Name); 29 | } 30 | else if (fieldType.IsArray) 31 | { 32 | ArrayLengthAttribute? arrayLengthAttribute = i.GetCustomAttribute(); 33 | size += arrayLengthAttribute.Length; 34 | } 35 | else 36 | { 37 | size += fieldType.SizeOf(version); 38 | } 39 | } 40 | return size; 41 | } 42 | private static int GetPrimitiveTypeSize(string name) 43 | { 44 | return name switch 45 | { 46 | "Int64" or "UInt64" => 8, 47 | "Int32" or "UInt32" => 4, 48 | "Int16" or "UInt16" => 2, 49 | _ => 0, 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Metadata/IL2CPP/MetadataClass.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Attributes; 2 | 3 | namespace MetadataConverter2.IL2CPP; 4 | public static class UnityIl2Cpp 5 | { 6 | public record GlobalMetadataHeader 7 | { 8 | public uint sanity; 9 | public int version; 10 | public uint stringLiteralOffset; // string data for managed code 11 | public int stringLiteralSize; 12 | public uint stringLiteralDataOffset; 13 | public int stringLiteralDataSize; 14 | public uint stringOffset; // string data for metadata 15 | public int stringSize; 16 | public uint eventsOffset; // EventDefinition 17 | public int eventsSize; 18 | public uint propertiesOffset; // PropertyDefinition 19 | public int propertiesSize; 20 | public uint methodsOffset; // MethodDefinition 21 | public int methodsSize; 22 | public uint parameterDefaultValuesOffset; // ParameterDefaultValue 23 | public int parameterDefaultValuesSize; 24 | public uint fieldDefaultValuesOffset; // FieldDefaultValue 25 | public int fieldDefaultValuesSize; 26 | public uint fieldAndParameterDefaultValueDataOffset; // uint8_t 27 | public int fieldAndParameterDefaultValueDataSize; 28 | public int fieldMarshaledSizesOffset; // FieldMarshaledSize 29 | public int fieldMarshaledSizesSize; 30 | public uint parametersOffset; // ParameterDefinition 31 | public int parametersSize; 32 | public uint fieldsOffset; // FieldDefinition 33 | public int fieldsSize; 34 | public uint genericParametersOffset; // GenericParameter 35 | public int genericParametersSize; 36 | public uint genericParameterConstraintsOffset; // TypeIndex 37 | public int genericParameterConstraintsSize; 38 | public uint genericContainersOffset; // GenericContainer 39 | public int genericContainersSize; 40 | public uint nestedTypesOffset; // TypeDefinitionIndex 41 | public int nestedTypesSize; 42 | public uint interfacesOffset; // TypeIndex 43 | public int interfacesSize; 44 | public uint vtableMethodsOffset; // EncodedMethodIndex 45 | public int vtableMethodsSize; 46 | public int interfaceOffsetsOffset; // InterfaceOffsetPair 47 | public int interfaceOffsetsSize; 48 | public uint typeDefinitionsOffset; // TypeDefinition 49 | public int typeDefinitionsSize; 50 | [Version(Max = 24.1)] 51 | public uint rgctxEntriesOffset; // RGCTXDefinition 52 | [Version(Max = 24.1)] 53 | public int rgctxEntriesCount; 54 | public uint imagesOffset; // ImageDefinition 55 | public int imagesSize; 56 | public uint assembliesOffset; // AssemblyDefinition 57 | public int assembliesSize; 58 | [Version(Min = 19, Max = 24.5)] 59 | public uint metadataUsageListsOffset; // MetadataUsageList 60 | [Version(Min = 19, Max = 24.5)] 61 | public int metadataUsageListsCount; 62 | [Version(Min = 19, Max = 24.5)] 63 | public uint metadataUsagePairsOffset; // MetadataUsagePair 64 | [Version(Min = 19, Max = 24.5)] 65 | public int metadataUsagePairsCount; 66 | [Version(Min = 19)] 67 | public uint fieldRefsOffset; // FieldRef 68 | [Version(Min = 19)] 69 | public int fieldRefsSize; 70 | [Version(Min = 20)] 71 | public int referencedAssembliesOffset; // int32_t 72 | [Version(Min = 20)] 73 | public int referencedAssembliesSize; 74 | [Version(Min = 21, Max = 27.2)] 75 | public uint attributesInfoOffset; // CustomAttributeTypeRange 76 | [Version(Min = 21, Max = 27.2)] 77 | public int attributesInfoCount; 78 | [Version(Min = 21, Max = 27.2)] 79 | public uint attributeTypesOffset; // TypeIndex 80 | [Version(Min = 21, Max = 27.2)] 81 | public int attributeTypesCount; 82 | [Version(Min = 29)] 83 | public uint attributeDataOffset; 84 | [Version(Min = 29)] 85 | public int attributeDataSize; 86 | [Version(Min = 29)] 87 | public uint attributeDataRangeOffset; 88 | [Version(Min = 29)] 89 | public int attributeDataRangeSize; 90 | [Version(Min = 22)] 91 | public int unresolvedVirtualCallParameterTypesOffset; // TypeIndex 92 | [Version(Min = 22)] 93 | public int unresolvedVirtualCallParameterTypesSize; 94 | [Version(Min = 22)] 95 | public int unresolvedVirtualCallParameterRangesOffset; // Range 96 | [Version(Min = 22)] 97 | public int unresolvedVirtualCallParameterRangesSize; 98 | [Version(Min = 23)] 99 | public int windowsRuntimeTypeNamesOffset; // WindowsRuntimeTypeNamePair 100 | [Version(Min = 23)] 101 | public int windowsRuntimeTypeNamesSize; 102 | [Version(Min = 27)] 103 | public int windowsRuntimeStringsOffset; // const char* 104 | [Version(Min = 27)] 105 | public int windowsRuntimeStringsSize; 106 | [Version(Min = 24)] 107 | public int exportedTypeDefinitionsOffset; // TypeDefinitionIndex 108 | [Version(Min = 24)] 109 | public int exportedTypeDefinitionsSize; 110 | } 111 | 112 | public record MetadataRegistration 113 | { 114 | public long genericClassesCount; 115 | public ulong genericClasses; 116 | public long genericInstsCount; 117 | public ulong genericInsts; 118 | public long genericMethodTableCount; 119 | public ulong genericMethodTable; 120 | public long typesCount; 121 | public ulong types; 122 | public long methodSpecsCount; 123 | public ulong methodSpecs; 124 | [Version(Max = 16)] 125 | public long methodReferencesCount; 126 | [Version(Max = 16)] 127 | public ulong methodReferences; 128 | 129 | public long fieldOffsetsCount; 130 | public ulong fieldOffsets; 131 | 132 | public long typeDefinitionsSizesCount; 133 | public ulong typeDefinitionsSizes; 134 | [Version(Min = 19)] 135 | public ulong metadataUsagesCount; 136 | [Version(Min = 19)] 137 | public ulong metadataUsages; 138 | } 139 | 140 | public record AssemblyDefinition 141 | { 142 | public int imageIndex; 143 | [Version(Min = 24.1)] 144 | public uint token; 145 | [Version(Max = 24)] 146 | public int customAttributeIndex; 147 | [Version(Min = 20)] 148 | public int referencedAssemblyStart; 149 | [Version(Min = 20)] 150 | public int referencedAssemblyCount; 151 | public AssemblyNameDefinition? aname; 152 | } 153 | 154 | public record AssemblyNameDefinition 155 | { 156 | public uint nameIndex; 157 | public uint cultureIndex; 158 | [Version(Max = 24.3)] 159 | public int hashValueIndex; 160 | public uint publicKeyIndex; 161 | public uint hash_alg; 162 | public int hash_len; 163 | public uint flags; 164 | public int major; 165 | public int minor; 166 | public int build; 167 | public int revision; 168 | [ArrayLength(Length = 8)] 169 | public byte[]? public_key_token; 170 | } 171 | 172 | public record ImageDefinition 173 | { 174 | public uint nameIndex; 175 | public int assemblyIndex; 176 | 177 | public int typeStart; 178 | public uint typeCount; 179 | 180 | [Version(Min = 24)] 181 | public int exportedTypeStart; 182 | [Version(Min = 24)] 183 | public uint exportedTypeCount; 184 | 185 | public int entryPointIndex; 186 | [Version(Min = 19)] 187 | public uint token; 188 | 189 | [Version(Min = 24.1)] 190 | public int customAttributeStart; 191 | [Version(Min = 24.1)] 192 | public uint customAttributeCount; 193 | } 194 | 195 | public record TypeDefinition 196 | { 197 | public uint nameIndex; 198 | public uint namespaceIndex; 199 | [Version(Max = 24)] 200 | public int customAttributeIndex; 201 | public int byvalTypeIndex; 202 | [Version(Max = 24.5)] 203 | public int byrefTypeIndex; 204 | 205 | public int declaringTypeIndex; 206 | public int parentIndex; 207 | public int elementTypeIndex; // we can probably remove this one. Only used for enums 208 | 209 | [Version(Max = 24.1)] 210 | public int rgctxStartIndex; 211 | [Version(Max = 24.1)] 212 | public int rgctxCount; 213 | 214 | public int genericContainerIndex; 215 | 216 | [Version(Max = 22)] 217 | public int delegateWrapperFromManagedToNativeIndex; 218 | [Version(Max = 22)] 219 | public int marshalingFunctionsIndex; 220 | [Version(Min = 21, Max = 22)] 221 | public int ccwFunctionIndex; 222 | [Version(Min = 21, Max = 22)] 223 | public int guidIndex; 224 | 225 | public uint flags; 226 | 227 | public int fieldStart; 228 | public int methodStart; 229 | public int eventStart; 230 | public int propertyStart; 231 | public int nestedTypesStart; 232 | public int interfacesStart; 233 | public int vtableStart; 234 | public int interfaceOffsetsStart; 235 | 236 | public ushort method_count; 237 | public ushort property_count; 238 | public ushort field_count; 239 | public ushort event_count; 240 | public ushort nested_type_count; 241 | public ushort vtable_count; 242 | public ushort interfaces_count; 243 | public ushort interface_offsets_count; 244 | 245 | // bitfield to portably encode boolean values as single bits 246 | // 01 - valuetype; 247 | // 02 - enumtype; 248 | // 03 - has_finalize; 249 | // 04 - has_cctor; 250 | // 05 - is_blittable; 251 | // 06 - is_import_or_windows_runtime; 252 | // 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) 253 | // 11 - PackingSize is default 254 | // 12 - ClassSize is default 255 | // 13-16 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) - the specified packing size (even for explicit layouts) 256 | public uint bitfield; 257 | [Version(Min = 19)] 258 | public uint token; 259 | 260 | public bool IsValueType => (bitfield & 0x1) == 1; 261 | public bool IsEnum => ((bitfield >> 1) & 0x1) == 1; 262 | } 263 | 264 | public record MethodDefinition 265 | { 266 | public uint nameIndex; 267 | public int declaringType; 268 | public int returnType; 269 | public int parameterStart; 270 | [Version(Max = 24)] 271 | public int customAttributeIndex; 272 | public int genericContainerIndex; 273 | [Version(Max = 24.1)] 274 | public int methodIndex; 275 | [Version(Max = 24.1)] 276 | public int invokerIndex; 277 | [Version(Max = 24.1)] 278 | public int delegateWrapperIndex; 279 | [Version(Max = 24.1)] 280 | public int rgctxStartIndex; 281 | [Version(Max = 24.1)] 282 | public int rgctxCount; 283 | public uint token; 284 | public ushort flags; 285 | public ushort iflags; 286 | public ushort slot; 287 | public ushort parameterCount; 288 | } 289 | 290 | public record ParameterDefinition 291 | { 292 | public uint nameIndex; 293 | public uint token; 294 | [Version(Max = 24)] 295 | public int customAttributeIndex; 296 | public int typeIndex; 297 | } 298 | 299 | public record FieldDefinition 300 | { 301 | public uint nameIndex; 302 | public int typeIndex; 303 | [Version(Max = 24)] 304 | public int customAttributeIndex; 305 | [Version(Min = 19)] 306 | public uint token; 307 | } 308 | 309 | public record FieldDefaultValue 310 | { 311 | public int fieldIndex; 312 | public int typeIndex; 313 | public int dataIndex; 314 | } 315 | 316 | public record PropertyDefinition 317 | { 318 | public uint nameIndex; 319 | public int get; 320 | public int set; 321 | public uint attrs; 322 | [Version(Max = 24)] 323 | public int customAttributeIndex; 324 | [Version(Min = 19)] 325 | public uint token; 326 | } 327 | 328 | public record CustomAttributeTypeRange 329 | { 330 | [Version(Min = 24.1)] 331 | public uint token; 332 | public int start; 333 | public int count; 334 | } 335 | 336 | public record MetadataUsageList 337 | { 338 | public uint start; 339 | public uint count; 340 | } 341 | 342 | public record MetadataUsagePair 343 | { 344 | public uint destinationIndex; 345 | public uint encodedSourceIndex; 346 | } 347 | 348 | public record StringLiteral 349 | { 350 | public uint length; 351 | public int dataIndex; 352 | } 353 | 354 | public record ParameterDefaultValue 355 | { 356 | public int parameterIndex; 357 | public int typeIndex; 358 | public int dataIndex; 359 | } 360 | 361 | public record EventDefinition 362 | { 363 | public uint nameIndex; 364 | public int typeIndex; 365 | public int add; 366 | public int remove; 367 | public int raise; 368 | [Version(Max = 24)] 369 | public int customAttributeIndex; 370 | [Version(Min = 19)] 371 | public uint token; 372 | } 373 | 374 | public record GenericContainer 375 | { 376 | /* index of the generic type definition or the generic method definition corresponding to this container */ 377 | public int ownerIndex; // either index into Class metadata array or MethodDefinition array 378 | public int type_argc; 379 | /* If true, we're a generic method, otherwise a generic type definition. */ 380 | public int is_method; 381 | /* Our type parameters. */ 382 | public int genericParameterStart; 383 | } 384 | 385 | public record FieldRef 386 | { 387 | public int typeIndex; 388 | public int fieldIndex; // local offset into type fields 389 | } 390 | 391 | public record GenericParameter 392 | { 393 | public int ownerIndex; /* Type or method this parameter was defined in. */ 394 | public uint nameIndex; 395 | public short constraintsStart; 396 | public short constraintsCount; 397 | public ushort num; 398 | public ushort flags; 399 | } 400 | 401 | public enum RGCTXDataType 402 | { 403 | _RGCTX_DATA_INVALID, 404 | _RGCTX_DATA_TYPE, 405 | _RGCTX_DATA_CLASS, 406 | _RGCTX_DATA_METHOD, 407 | _RGCTX_DATA_ARRAY, 408 | _RGCTX_DATA_CONSTRAINED, 409 | } 410 | 411 | public record RGCTXDefinitionData 412 | { 413 | public int rgctxDataDummy; 414 | public int methodIndex => rgctxDataDummy; 415 | public int typeIndex => rgctxDataDummy; 416 | } 417 | 418 | public record RGCTXDefinition 419 | { 420 | public RGCTXDataType type => type_post29 == 0 ? (RGCTXDataType)type_pre29 : (RGCTXDataType)type_post29; 421 | [Version(Max = 27.1)] 422 | public int type_pre29; 423 | [Version(Min = 29)] 424 | public ulong type_post29; 425 | [Version(Max = 27.1)] 426 | public RGCTXDefinitionData? data; 427 | [Version(Min = 27.2)] 428 | public ulong _data; 429 | } 430 | 431 | public enum MetadataUsage 432 | { 433 | kMetadataUsageInvalid, 434 | kMetadataUsageTypeInfo, 435 | kMetadataUsageType, 436 | kMetadataUsageMethodDef, 437 | kMetadataUsageFieldInfo, 438 | kMetadataUsageStringLiteral, 439 | kMetadataUsageMethodRef, 440 | }; 441 | 442 | public record CustomAttributeDataRange 443 | { 444 | public uint token; 445 | public uint startOffset; 446 | } 447 | } -------------------------------------------------------------------------------- /Metadata/IL2CPP/MhyMetadataClass.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Attributes; 2 | 3 | namespace MetadataConverter2.IL2CPP; 4 | public static class MhyIl2Cpp 5 | { 6 | public record GlobalMetadataHeader 7 | { 8 | public uint sanity; // 0x00 9 | public int version; 10 | public uint seedPart50; // 0x08 11 | public uint seedPart51; 12 | public uint seedPart52; // 0x10 13 | public uint seedPart53; 14 | public uint stringLiteralDataOffset; // 0x18 15 | public int stringLiteralDataSize; 16 | public uint stringLiteralOffset; // 0x20 17 | public int stringLiteralSize; 18 | public uint genericContainersOffset; // 0x28 19 | public int genericContainersSize; 20 | public uint nestedTypesOffset; // 0x30 21 | public int nestedTypesSize; 22 | public uint interfacesOffset; // 0x38 23 | public int interfacesSize; 24 | public uint vtableMethodsOffset; // 0x40 25 | public int vtableMethodsSize; 26 | public int interfaceOffsetsOffset; // 0x48 27 | public int interfaceOffsetsSize; 28 | public uint typeDefinitionsOffset; // 0x50 29 | public int typeDefinitionsSize; 30 | [Version(Max = 24.1)] 31 | public uint rgctxEntriesOffset; // 0x58 32 | [Version(Max = 24.1)] 33 | public int rgctxEntriesCount; 34 | [Version(Min = 24.4)] 35 | public uint unk58; // 0x58 36 | [Version(Min = 24.4)] 37 | public int unk60; 38 | public uint seedPart10; //0x60 39 | public uint seedPart11; 40 | public uint seedPart12; //0x68 41 | public uint seedPart13; 42 | public uint imagesOffset; // 0x70 43 | public int imagesSize; 44 | public uint assembliesOffset; // 0x78 45 | public int assembliesSize; 46 | public uint fieldsOffset; // 0x80 47 | public int fieldsSize; 48 | public uint genericParametersOffset; // 0x88 49 | public int genericParametersSize; 50 | public uint fieldAndParameterDefaultValueDataOffset; // 0x90 51 | public int fieldAndParameterDefaultValueDataSize; 52 | public uint fieldMarshaledSizesOffset; // 0x98 53 | public int fieldMarshaledSizesSize; 54 | [Version(Min = 20)] 55 | public int referencedAssembliesOffset; // 0xA0 56 | [Version(Min = 20)] 57 | public int referencedAssembliesSize; 58 | [Version(Min = 21, Max = 27.2)] 59 | public uint attributesInfoOffset; // 0xA8 60 | [Version(Min = 21, Max = 27.2)] 61 | public int attributesInfoCount; 62 | [Version(Min = 21, Max = 27.2)] 63 | public uint attributeTypesOffset; // 0xB0 64 | [Version(Min = 21, Max = 27.2)] 65 | public int attributeTypesCount; 66 | [Version(Min = 22)] 67 | public int unresolvedVirtualCallParameterTypesOffset; // 0xB8 68 | [Version(Min = 22)] 69 | public int unresolvedVirtualCallParameterTypesSize; 70 | [Version(Min = 22)] 71 | public int unresolvedVirtualCallParameterRangesOffset; // 0xC0 72 | [Version(Min = 22)] 73 | public int unresolvedVirtualCallParameterRangesSize; 74 | [Version(Min = 23)] 75 | public int windowsRuntimeTypeNamesOffset; // 0xC8 76 | [Version(Min = 23)] 77 | public int windowsRuntimeTypeNamesSize; 78 | [Version(Min = 24)] 79 | public int exportedTypeDefinitionsOffset; // 0xD0 80 | [Version(Min = 24)] 81 | public int exportedTypeDefinitionsSize; 82 | public uint stringOffset; // 0xD8 83 | public int stringSize; 84 | public uint parametersOffset; // 0xE0 85 | public int parametersSize; 86 | public uint genericParameterConstraintsOffset; // 0xE8 87 | public int genericParameterConstraintsSize; 88 | public uint seedPart40; // 0xF0 89 | public uint seedPart41; 90 | [Version(Min = 19, Max = 24.5)] 91 | public uint metadataUsagePairsOffset; // 0xF8 92 | [Version(Min = 19, Max = 24.5)] 93 | public int metadataUsagePairsCount; 94 | public uint seedPart30; // 0x100 95 | public uint seedPart31; 96 | public uint seedPart32; // 0x108 97 | public uint seedPart33; 98 | [Version(Min = 19)] 99 | public uint fieldRefsOffset; // 0x110 100 | [Version(Min = 19)] 101 | public int fieldRefsSize; 102 | public uint eventsOffset; // 0x118 103 | public int eventsSize; 104 | public uint propertiesOffset; // 0x120 105 | public int propertiesSize; 106 | public uint methodsOffset; // 0x128 107 | public int methodsSize; 108 | public uint parameterDefaultValuesOffset; // 0x130 109 | public int parameterDefaultValuesSize; 110 | public uint fieldDefaultValuesOffset; // 0x138 111 | public int fieldDefaultValuesSize; 112 | public uint seedPart20; // 0x140 113 | public uint seedPart21; 114 | public uint seedPart22; // 0x148 115 | public uint seedPart23; 116 | [Version(Min = 19, Max = 24.5)] 117 | public uint metadataUsageListsOffset; // 0x150 118 | [Version(Min = 19, Max = 24.5)] 119 | public int metadataUsageListsCount; 120 | } 121 | 122 | public record TypeDefinition 123 | { 124 | public uint nameIndex; 125 | public uint namespaceIndex; 126 | [Version(Max = 24.1)] 127 | public int customAttributeIndex; 128 | public int byvalTypeIndex; 129 | [Version(Max = 24.5)] 130 | public int byrefTypeIndex; 131 | 132 | public int declaringTypeIndex; 133 | public int parentIndex; 134 | public int elementTypeIndex; // we can probably remove this one. Only used for enums 135 | 136 | [Version(Max = 24.1)] 137 | public int rgctxStartIndex; 138 | [Version(Max = 24.1)] 139 | public int rgctxCount; 140 | 141 | public int genericContainerIndex; 142 | 143 | public uint flags; 144 | 145 | public int fieldStart; 146 | public int propertyStart; 147 | public int methodStart; 148 | public int eventStart; 149 | public int nestedTypesStart; 150 | public int interfacesStart; 151 | public int interfaceOffsetsStart; 152 | public int vtableStart; 153 | 154 | public ushort event_count; 155 | public ushort method_count; 156 | public ushort property_count; 157 | public ushort field_count; 158 | public ushort vtable_count; 159 | public ushort interfaces_count; 160 | public ushort interface_offsets_count; 161 | public ushort nested_type_count; 162 | 163 | // bitfield to portably encode boolean values as single bits 164 | // 01 - valuetype; 165 | // 02 - enumtype; 166 | // 03 - has_finalize; 167 | // 04 - has_cctor; 168 | // 05 - is_blittable; 169 | // 06 - is_import_or_windows_runtime; 170 | // 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) 171 | // 11 - PackingSize is default 172 | // 12 - ClassSize is default 173 | // 13-16 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) - the specified packing size (even for explicit layouts) 174 | public uint bitfield; 175 | [Version(Min = 19)] 176 | public uint token; 177 | 178 | public bool IsValueType => (bitfield & 0x1) == 1; 179 | public bool IsEnum => ((bitfield >> 1) & 0x1) == 1; 180 | } 181 | 182 | public record MethodDefinition 183 | { 184 | public int returnType; 185 | public int declaringType; 186 | public uint unk08; 187 | public uint nameIndex; 188 | public int parameterStart; 189 | public int genericContainerIndex; 190 | [Version(Max = 24)] 191 | public int customAttributeIndex; 192 | [Version(Max = 24.1)] 193 | public int delegateWrapperIndex; 194 | public uint unk20; 195 | [Version(Max = 24.1)] 196 | public int methodIndex; 197 | [Version(Max = 24.1)] 198 | public int invokerIndex; 199 | [Version(Max = 24.1)] 200 | public int rgctxCount; 201 | [Version(Max = 24.1)] 202 | public int rgctxStartIndex; 203 | public ushort parameterCount; 204 | public ushort flags; 205 | public ushort slot; 206 | public ushort iflags; 207 | public uint token; 208 | } 209 | 210 | public record FieldDefinition 211 | { 212 | [Version(Max = 24)] 213 | public int customAttributeIndex; 214 | public int typeIndex; 215 | public uint nameIndex; 216 | [Version(Min = 19)] 217 | public uint token; 218 | } 219 | 220 | public record PropertyDefinition 221 | { 222 | [Version(Max = 24)] 223 | public int customAttributeIndex; 224 | public uint nameIndex; 225 | public uint unk08; 226 | [Version(Min = 19)] 227 | public uint token; 228 | public uint attrs; 229 | public uint unk14; 230 | public int set; 231 | public int get; 232 | } 233 | 234 | public record StringLiteral 235 | { 236 | public int dataIndex; 237 | public uint length; 238 | } 239 | 240 | public record MhyUsages 241 | { 242 | public ulong typeInfoUsageCount; 243 | public ulong typeInfoUsage; 244 | public ulong methodDefRefUsageCount; 245 | public ulong methodDefRefUsage; 246 | public ulong fieldInfoUsageCount; 247 | public ulong fieldInfoUsage; 248 | public ulong stringLiteralUsageCount; 249 | public ulong stringLiteralUsage; 250 | 251 | public ulong GetAddress(UnityIl2Cpp.MetadataUsage usage, ulong index) 252 | { 253 | var baseAddress = usage switch 254 | { 255 | UnityIl2Cpp.MetadataUsage.kMetadataUsageTypeInfo => typeInfoUsage, 256 | UnityIl2Cpp.MetadataUsage.kMetadataUsageFieldInfo => fieldInfoUsage, 257 | UnityIl2Cpp.MetadataUsage.kMetadataUsageStringLiteral => stringLiteralUsage, 258 | UnityIl2Cpp.MetadataUsage.kMetadataUsageMethodDef or UnityIl2Cpp.MetadataUsage.kMetadataUsageMethodRef => methodDefRefUsage, 259 | _ => throw new NotImplementedException(), 260 | }; 261 | 262 | return baseAddress + (index * 8); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /Metadata/Managers/MetaManager.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Converters; 2 | using MetadataConverter2.MetaTypes; 3 | using static MetadataConverter2.Crypto.CryptoHelper; 4 | 5 | namespace MetadataConverter2.Managers; 6 | public static class MetaManager 7 | { 8 | private static readonly Dictionary Metas = new(); 9 | static MetaManager() 10 | { 11 | int index = 0; 12 | Metas.Add(index++, new Blocks(MetaType.GI, GIInitVector, 24)); 13 | Metas.Add(index++, new Mark(MetaType.GICB1, 24)); 14 | Metas.Add(index++, new Blocks(MetaType.GICBX, new byte[0x10], 24)); 15 | Metas.Add(index++, new Usages(MetaType.GIV2, GIInitVector, 24.5, BlocksConverter.Convert)); 16 | Metas.Add(index++, new Blocks(MetaType.BH3Pre, new byte[0x10], 24)); 17 | Metas.Add(index++, new Blocks(MetaType.BH3, BH3InitVector, 24)); 18 | Metas.Add(index++, new Usages(MetaType.BH3V2, BH3InitVector, 24.5, BlocksConverter.Convert)); 19 | Metas.Add(index++, new Struct(MetaType.SR, SRInitVector, 24.5)); 20 | Metas.Add(index++, new Usages(MetaType.SRV2, SRInitVector, 24.5, StructConverter.Convert)); 21 | Metas.Add(index++, new Struct(MetaType.ZZZ, Array.Empty(), 24.5)); 22 | } 23 | public static MetaBase GetMeta(MetaType metaType) 24 | { 25 | return GetMeta((int)metaType); 26 | } 27 | 28 | public static MetaBase GetMeta(int index) 29 | { 30 | return !Metas.TryGetValue(index, out MetaBase? meta) ? throw new ArgumentException("Invalid meta !!") : meta; 31 | } 32 | 33 | public static MetaBase GetMeta(string name) 34 | { 35 | return Metas.FirstOrDefault(x => x.Value.Name == name).Value; 36 | } 37 | 38 | public static MetaBase[] GetMetas() 39 | { 40 | return Metas.Values.ToArray(); 41 | } 42 | 43 | public static string[] GetMetaNames() 44 | { 45 | return Metas.Values.Select(x => x.Name).ToArray(); 46 | } 47 | 48 | public static string SupportedMetas() 49 | { 50 | return $"Supported Metas:\n{string.Join("\n", Metas.Values.Select(x => x.Name))}"; 51 | } 52 | } -------------------------------------------------------------------------------- /Metadata/MetaTypes/Blocks.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Converters; 2 | using MetadataConverter2.Crypto; 3 | using MetadataConverter2.Extensions; 4 | using MetadataConverter2.Utils; 5 | using System.Buffers.Binary; 6 | using System.Security.Cryptography; 7 | using static MetadataConverter2.IL2CPP.MhyIl2Cpp; 8 | 9 | namespace MetadataConverter2.MetaTypes; 10 | public record Blocks : MetaBase 11 | { 12 | private readonly byte[] _initVector; 13 | private readonly bool _encrypted; 14 | 15 | public Blocks(MetaType type, byte[] initVector, double version) : base(type, version) 16 | { 17 | _initVector = initVector; 18 | _encrypted = _initVector.Length != 0; 19 | } 20 | public override bool Convert(MemoryStream stream) 21 | { 22 | return BlocksConverter.Convert(stream, Version); 23 | } 24 | 25 | public override bool Decrypt(MemoryStream stream) 26 | { 27 | if (_encrypted) 28 | { 29 | Console.WriteLine("Decrypting blocks..."); 30 | long metadataSize = DecryptBlocks(stream); 31 | Console.WriteLine("Decrypting stringLiterals..."); 32 | DecryptStrings(stream); 33 | stream.SetLength(metadataSize); 34 | } 35 | return true; 36 | } 37 | private long DecryptBlocks(MemoryStream stream) 38 | { 39 | BinaryStream bs = new(stream); 40 | long metadataSize = InitKeys(bs, out byte[]? iv); 41 | 42 | ulong blocksCount = bs.Length / 0x100; 43 | ulong blockSize = blocksCount - (blocksCount % 0x40); 44 | 45 | byte[] buffer = new byte[0x40]; 46 | AES128_CBC decryptor = AES128_CBC.CreateDecryptor(iv); 47 | for (ulong pos = 0; pos < bs.Length; pos += blockSize) 48 | { 49 | bs.Position = pos; 50 | using CryptoStream cryptoStream = new(stream, decryptor, CryptoStreamMode.Read, true); 51 | _ = cryptoStream.Read(buffer); 52 | bs.Position = pos; 53 | bs.Write(buffer); 54 | decryptor.Reset(); 55 | } 56 | 57 | bs.Position = 0; 58 | return metadataSize; 59 | } 60 | private void DecryptStrings(MemoryStream stream) 61 | { 62 | BinaryStream bs = new(stream) 63 | { 64 | Version = Version 65 | }; 66 | GlobalMetadataHeader header = bs.ReadClass(); 67 | 68 | DecryptStringInfo(bs, header, out byte[]? key); 69 | 70 | bs.Position = 0x18; 71 | bs.Write(header.stringLiteralDataOffset); 72 | bs.Write(header.stringLiteralDataSize); 73 | bs.Write(header.stringLiteralOffset); 74 | 75 | bs.Position = 0xD8; 76 | bs.Write(header.stringOffset); 77 | bs.Write(header.stringSize); 78 | 79 | StringLiteral[] stringLiterals = bs.ReadMetadataClassArray(header.stringLiteralOffset, header.stringLiteralSize); 80 | for (int i = 0; i < stringLiterals.Length; i++) 81 | { 82 | StringLiteral stringLiteral = stringLiterals[i]; 83 | uint offset = header.stringLiteralDataOffset + (uint)stringLiteral.dataIndex; 84 | 85 | if (offset + stringLiteral.length > stream.Length) 86 | { 87 | throw new IndexOutOfRangeException("String Offset Out of Bound"); 88 | } 89 | 90 | bs.Position = offset; 91 | for (int j = 0; j < stringLiteral.length; j++) 92 | { 93 | byte b = bs.ReadByte(); 94 | byte cl = key[(j + (key.Length / 4)) % key.Length]; 95 | int al = key[(j % (key.Length / 2)) + (i % (key.Length / 2))] + j; 96 | 97 | bs.Position -= 1; 98 | bs.Write((byte)(b ^ cl ^ al)); 99 | } 100 | } 101 | 102 | bs.Position = 0; 103 | } 104 | private long InitKeys(BinaryStream bs, out byte[] iv) 105 | { 106 | ulong blocksOffset = bs.Length - 0x4000; 107 | 108 | bs.Position = blocksOffset - 8; 109 | long metadataSize = bs.ReadInt64(); 110 | if (metadataSize > (long)bs.Length) 111 | { 112 | throw new ArgumentException("Invalid Metadata Length"); 113 | } 114 | 115 | bs.Position += 0xC8; 116 | uint signature = bs.ReadUInt32(); 117 | if (signature != 0x2CFEFC2E) 118 | { 119 | throw new InvalidDataException("Invalid Metadata Signature"); 120 | } 121 | 122 | bs.Position += 0x06; 123 | ushort keysOffset = bs.ReadUInt16(); 124 | 125 | bs.Position = blocksOffset + keysOffset; 126 | 127 | iv = bs.ReadBytes(0x10); 128 | byte[] keys = bs.ReadBytes(0xB00); 129 | 130 | bs.Position = blocksOffset + 0x3000; 131 | 132 | byte[] ivKey = bs.ReadBytes(0x10); 133 | byte[] keysKey = bs.ReadBytes(0xB00); 134 | 135 | for (int i = 0; i < iv.Length; i++) 136 | { 137 | iv[i] ^= ivKey[i]; 138 | } 139 | for (int i = 0; i < keys.Length; i++) 140 | { 141 | keys[i] ^= (byte)(keysKey[i] ^ iv[i % 0x10]); 142 | } 143 | 144 | for (int i = 0; i < iv.Length; i++) 145 | { 146 | iv[i] ^= _initVector[i]; 147 | } 148 | for (int i = 0; i < AES128_CBC.ExpansionKey.Length; i++) 149 | { 150 | byte b = 0; 151 | for (int j = 0; j < 0x10; j++) 152 | { 153 | b ^= keys.AsSpan(i * 0x10, 0x10)[j]; 154 | } 155 | AES128_CBC.ExpansionKey[i] = b; 156 | } 157 | 158 | return metadataSize; 159 | } 160 | private static void DecryptStringInfo(BinaryStream stream, GlobalMetadataHeader header, out byte[] key) 161 | { 162 | uint[] seedTable = new uint[] 163 | { 164 | header.seedPart10, header.seedPart11, header.seedPart12, header.seedPart13, 165 | header.seedPart20, header.seedPart21, header.seedPart22, header.seedPart23, 166 | header.seedPart30, header.seedPart31, header.seedPart32, header.seedPart33, 167 | header.seedPart40, header.seedPart41, 168 | header.seedPart50, header.seedPart51, header.seedPart52, header.seedPart53, 169 | }; 170 | 171 | uint firstSeed = seedTable[0]; 172 | uint lastSeed = seedTable[^1]; 173 | 174 | ulong lowerSeed = seedTable[(lastSeed & 0x0F) + 0x02]; 175 | ulong upperSeed = seedTable[firstSeed & 0x0F]; 176 | 177 | ulong seed = (upperSeed << 0x20) | lowerSeed; 178 | MT19937_64 mt = new(seed); 179 | 180 | header.stringSize ^= (int)mt.Int64(); 181 | header.stringOffset ^= (uint)mt.Int64(); 182 | 183 | _ = mt.Int64(); 184 | header.stringLiteralOffset ^= (uint)mt.Int64(); 185 | header.stringLiteralDataSize ^= (int)mt.Int64(); 186 | header.stringLiteralDataOffset ^= (uint)mt.Int64(); 187 | 188 | key = new byte[0x5000]; 189 | for (int i = 0; i < key.Length; i += 0x08) 190 | { 191 | BinaryPrimitives.WriteUInt64LittleEndian(key.AsSpan(i), mt.Int64()); 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /Metadata/MetaTypes/Mark.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Utils; 2 | using static MetadataConverter2.Crypto.CryptoHelper; 3 | 4 | namespace MetadataConverter2.MetaTypes 5 | { 6 | public record Mark : MetaBase 7 | { 8 | private const int BlockSize = 0xA00; 9 | private const int ChunkSize = 0x264; 10 | private const int ChunkPadding = 4; 11 | 12 | private static readonly int BlockPadding = ((BlockSize / ChunkSize) + 1) * ChunkPadding; 13 | private static readonly int ChunkSizeWithPadding = ChunkSize + ChunkPadding; 14 | private static readonly int BlockSizeWithPadding = BlockSize + BlockPadding; 15 | 16 | public Mark(MetaType type, double version) : base(type, version) { } 17 | 18 | public override bool Convert(MemoryStream stream) => true; 19 | public override bool Decrypt(MemoryStream stream) 20 | { 21 | using BinaryStream bs = new(stream); 22 | string signature = bs.ReadStringToNull(0, 4); 23 | if (signature != "mark") 24 | { 25 | throw new InvalidDataException("Invalid Metadata Signature"); 26 | } 27 | 28 | int index = 0; 29 | byte[] block = new byte[BlockSizeWithPadding]; 30 | byte[] chunk = new byte[ChunkSizeWithPadding]; 31 | MemoryStream dataStream = new(); 32 | while (bs.Length != bs.Position) 33 | { 34 | int readBlockBytes = bs.Read(block, 0, block.Length); 35 | using MemoryStream blockStream = new(block, 0, readBlockBytes); 36 | while (blockStream.Length != blockStream.Position) 37 | { 38 | int readChunkBytes = blockStream.Read(chunk); 39 | if (readBlockBytes == BlockSizeWithPadding || readChunkBytes == ChunkSizeWithPadding) 40 | { 41 | readChunkBytes -= ChunkPadding; 42 | } 43 | for (int i = 0; i < readChunkBytes; i++) 44 | { 45 | chunk[i] ^= MarkKey[index++ % MarkKey.Length]; 46 | } 47 | dataStream.Write(chunk, 0, readChunkBytes); 48 | } 49 | } 50 | 51 | stream.Position = 0; 52 | dataStream.Position = 0; 53 | dataStream.CopyTo(stream); 54 | stream.SetLength(dataStream.Length); 55 | return true; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Metadata/MetaTypes/MetaBase.cs: -------------------------------------------------------------------------------- 1 | namespace MetadataConverter2.MetaTypes 2 | { 3 | public abstract record MetaBase 4 | { 5 | public string Name { get; set; } 6 | public MetaType Type { get; } 7 | public double Version { get; } 8 | public MetaBase(MetaType type, double version) 9 | { 10 | Name = type.ToString(); 11 | Type = type; 12 | Version = version; 13 | } 14 | 15 | public abstract bool Decrypt(MemoryStream stream); 16 | public abstract bool Convert(MemoryStream stream); 17 | 18 | public sealed override string ToString() 19 | { 20 | return Name; 21 | } 22 | } 23 | 24 | public enum MetaType 25 | { 26 | GI, 27 | GICB1, 28 | GICBX, 29 | GIV2, 30 | BH3Pre, 31 | BH3, 32 | BH3V2, 33 | SR, 34 | SRV2, 35 | ZZZ, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Metadata/MetaTypes/Struct.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Converters; 2 | 3 | namespace MetadataConverter2.MetaTypes 4 | { 5 | public record Struct : Blocks 6 | { 7 | public Struct(MetaType type, byte[] initVector, double version) : base(type, initVector, version) { } 8 | 9 | public override bool Convert(MemoryStream stream) 10 | { 11 | return StructConverter.Convert(stream, Version); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Metadata/MetaTypes/Usages.cs: -------------------------------------------------------------------------------- 1 | using AsmResolver.PE.File; 2 | using MetadataConverter2.Extensions; 3 | using MetadataConverter2.IL2CPP; 4 | using MetadataConverter2.Utils; 5 | using static MetadataConverter2.IL2CPP.UnityIl2Cpp; 6 | 7 | namespace MetadataConverter2.MetaTypes; 8 | public record Usages : Blocks 9 | { 10 | private string il2cpp_path = string.Empty; 11 | private Func Converter; 12 | 13 | private PEFile PEFile; 14 | private ulong[] metadataUsages; 15 | private CodegenRegistration Codegen; 16 | private MetadataUsageList[] metadataUsageLists; 17 | private MetadataUsagePair[] metadataUsagePairs; 18 | 19 | public Usages(MetaType type, byte[] initVector, double version, Func converter) : base(type, initVector, version) 20 | { 21 | Converter = converter; 22 | } 23 | public override bool Convert(MemoryStream stream) 24 | { 25 | if (string.IsNullOrEmpty(il2cpp_path)) 26 | { 27 | Console.WriteLine("Please enter path to il2cpp binary to be patched:"); 28 | string path = Console.ReadLine().Trim('\"'); 29 | if (!File.Exists(path)) 30 | { 31 | throw new FileNotFoundException("Unable to find file at given path", path); 32 | } 33 | 34 | il2cpp_path = path; 35 | } 36 | 37 | byte[] bytes = File.ReadAllBytes(il2cpp_path); 38 | PEFile = PEFile.FromBytes(bytes); 39 | 40 | if (!PEFile.TryGetCodegenRegisteration(Version, out Codegen)) 41 | { 42 | Console.WriteLine("Unable to find codegen !!"); 43 | return false; 44 | } 45 | 46 | Console.WriteLine($"Found codegen at 0x{PEFile.GetVA(Codegen.Rva):X8} !!"); 47 | 48 | Console.WriteLine("Applying Usages..."); 49 | 50 | ApplyUsages(stream); 51 | 52 | var fileInfo = new FileInfo(il2cpp_path); 53 | string outputPath = Path.Combine(fileInfo.Directory.FullName, $"{Path.GetFileNameWithoutExtension(fileInfo.Name)}_patched{fileInfo.Extension}"); 54 | 55 | Console.WriteLine($"Patching {fileInfo.Name}..."); 56 | Codegen.Patch(PEFile, Version, metadataUsages); 57 | PEFile.Write(outputPath); 58 | 59 | Console.WriteLine($"Generated patched il2cpp binary at {outputPath} !!"); 60 | return true; 61 | } 62 | private void ApplyUsages(MemoryStream stream) 63 | { 64 | using BinaryStream bs = new(stream, true) { Version = Version }; 65 | 66 | MhyIl2Cpp.GlobalMetadataHeader header = bs.ReadClass(0); 67 | 68 | metadataUsagePairs = bs.ReadMetadataClassArray(header.metadataUsagePairsOffset, header.metadataUsagePairsCount); 69 | metadataUsageLists = new MetadataUsageList[] { new MetadataUsageList() { start = 0, count = (uint)metadataUsagePairs.Length } }; 70 | 71 | uint i = 0; 72 | var usages = new Dictionary(); 73 | foreach (var metadataUsagePair in metadataUsagePairs) 74 | { 75 | var usage = (MetadataUsage)GetEncodedIndexType(metadataUsagePair.encodedSourceIndex); 76 | 77 | var address = Codegen.Usages.GetAddress(usage, metadataUsagePair.destinationIndex); 78 | if (usages.TryGetValue(address, out var index)) 79 | { 80 | metadataUsagePair.destinationIndex = index; 81 | } 82 | else 83 | { 84 | usages[address] = i; 85 | metadataUsagePair.destinationIndex = i++; 86 | } 87 | } 88 | 89 | metadataUsages = usages.Keys.ToArray(); 90 | 91 | Converter(stream, Version, metadataUsagePairs, metadataUsageLists); 92 | } 93 | private static uint GetEncodedIndexType(uint index) 94 | { 95 | return (index & 0xE0000000) >> 29; 96 | } 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /Metadata/Metadata.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Metadata/Utils/BinaryStream.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.Attributes; 2 | using System.Reflection; 3 | using System.Text; 4 | 5 | namespace MetadataConverter2.Utils; 6 | public class BinaryStream : IDisposable 7 | { 8 | public double Version; 9 | public bool Is32Bit; 10 | private readonly bool _leaveOpen; 11 | private readonly Stream stream; 12 | private readonly BinaryReader reader; 13 | private readonly BinaryWriter writer; 14 | private readonly MethodInfo readClass; 15 | private readonly MethodInfo readClassArray; 16 | private readonly MethodInfo writeClass; 17 | private readonly MethodInfo writeClassArray; 18 | private readonly Dictionary genericMethodCache = new(); 19 | private readonly Dictionary attributeCache = new(); 20 | 21 | public BinaryStream(Stream input, bool leaveOpen = false) 22 | { 23 | stream = input; 24 | _leaveOpen = leaveOpen; 25 | reader = new BinaryReader(stream, Encoding.UTF8, true); 26 | writer = new BinaryWriter(stream, Encoding.UTF8, true); 27 | readClass = GetType().GetMethod("ReadClass", Type.EmptyTypes); 28 | readClassArray = GetType().GetMethod("ReadClassArray", new[] { typeof(long) }); 29 | writeClass = GetType().GetMethod("WriteClass", Type.EmptyTypes); 30 | writeClassArray = GetType().GetMethod("WriteClassArray", new[] { typeof(long) }); 31 | } 32 | 33 | public int Read(byte[] buffer, int index, int count) 34 | { 35 | return reader.Read(buffer, index, count); 36 | } 37 | 38 | public byte ReadByte() 39 | { 40 | return reader.ReadByte(); 41 | } 42 | 43 | public byte[] ReadBytes(int count) 44 | { 45 | return reader.ReadBytes(count); 46 | } 47 | 48 | public short ReadInt16() 49 | { 50 | return reader.ReadInt16(); 51 | } 52 | 53 | public ushort ReadUInt16() 54 | { 55 | return reader.ReadUInt16(); 56 | } 57 | 58 | public int ReadInt32() 59 | { 60 | return reader.ReadInt32(); 61 | } 62 | 63 | public uint ReadUInt32() 64 | { 65 | return reader.ReadUInt32(); 66 | } 67 | 68 | public long ReadInt64() 69 | { 70 | return reader.ReadInt64(); 71 | } 72 | 73 | public ulong ReadUInt64() 74 | { 75 | return reader.ReadUInt64(); 76 | } 77 | 78 | public void Write(byte[] buffer, int index, int count) 79 | { 80 | writer.Write(buffer, index, count); 81 | } 82 | 83 | public void Write(byte value) 84 | { 85 | writer.Write(value); 86 | } 87 | 88 | public void Write(short value) 89 | { 90 | writer.Write(value); 91 | } 92 | 93 | public void Write(ushort value) 94 | { 95 | writer.Write(value); 96 | } 97 | 98 | public void Write(int value) 99 | { 100 | writer.Write(value); 101 | } 102 | 103 | public void Write(uint value) 104 | { 105 | writer.Write(value); 106 | } 107 | 108 | public void Write(long value) 109 | { 110 | writer.Write(value); 111 | } 112 | 113 | public void Write(ulong value) 114 | { 115 | writer.Write(value); 116 | } 117 | 118 | public void Write(byte[] value) 119 | { 120 | writer.Write(value); 121 | } 122 | 123 | public ulong Position 124 | { 125 | get => (ulong)stream.Position; 126 | set => stream.Position = (long)value; 127 | } 128 | 129 | public ulong Length => (ulong)stream.Length; 130 | 131 | public string ReadStringToNull(ulong addr, int count = 0x7FFF) 132 | { 133 | Position = addr; 134 | List bytes = new(); 135 | int i = 0; 136 | while (i < count && Position < Length) 137 | { 138 | byte b = ReadByte(); 139 | if (b == 0) 140 | { 141 | break; 142 | } 143 | 144 | bytes.Add(b); 145 | i++; 146 | } 147 | return Encoding.UTF8.GetString(bytes.ToArray()); 148 | } 149 | public long ReadIntPtr() 150 | { 151 | return Is32Bit ? ReadInt32() : ReadInt64(); 152 | } 153 | 154 | public virtual ulong ReadUIntPtr() 155 | { 156 | return Is32Bit ? ReadUInt32() : ReadUInt64(); 157 | } 158 | 159 | public ulong PointerSize => Is32Bit ? 4ul : 8ul; 160 | 161 | private object ReadPrimitive(Type type) 162 | { 163 | string typename = type.Name; 164 | return typename switch 165 | { 166 | "Int32" => ReadInt32(), 167 | "UInt32" => ReadUInt32(), 168 | "Int16" => ReadInt16(), 169 | "UInt16" => ReadUInt16(), 170 | "Byte" => ReadByte(), 171 | "Int64" => ReadInt64(), 172 | "UInt64" => ReadUInt64(), 173 | _ => throw new NotSupportedException(), 174 | }; 175 | } 176 | 177 | private void WritePrimitive(Type type, object value) 178 | { 179 | string typename = type.Name; 180 | switch (typename) 181 | { 182 | case "Int32": 183 | Write((int)value); 184 | break; 185 | case "UInt32": 186 | Write((uint)value); 187 | break; 188 | case "Int16": 189 | Write((short)value); 190 | break; 191 | case "UInt16": 192 | Write((ushort)value); 193 | break; 194 | case "Byte": 195 | Write((byte)value); 196 | break; 197 | case "Int64": 198 | Write((long)value); 199 | break; 200 | case "UInt64": 201 | Write((ulong)value); 202 | break; 203 | default: 204 | throw new NotSupportedException(); 205 | } 206 | } 207 | 208 | public T ReadClass(ulong addr) where T : new() 209 | { 210 | Position = addr; 211 | return ReadClass(); 212 | } 213 | 214 | public T ReadClass() where T : new() 215 | { 216 | Type type = typeof(T); 217 | if (type.IsPrimitive) 218 | { 219 | return (T)ReadPrimitive(type); 220 | } 221 | else 222 | { 223 | T t = new(); 224 | foreach (FieldInfo i in t.GetType().GetFields()) 225 | { 226 | if (!attributeCache.TryGetValue(i, out VersionAttribute[]? versionAttributes)) 227 | { 228 | if (Attribute.IsDefined(i, typeof(VersionAttribute))) 229 | { 230 | versionAttributes = i.GetCustomAttributes().ToArray(); 231 | attributeCache.Add(i, versionAttributes); 232 | } 233 | } 234 | if (versionAttributes?.Length > 0) 235 | { 236 | bool read = false; 237 | foreach (VersionAttribute versionAttribute in versionAttributes) 238 | { 239 | if (Version >= versionAttribute.Min && Version <= versionAttribute.Max) 240 | { 241 | read = true; 242 | break; 243 | } 244 | } 245 | if (!read) 246 | { 247 | continue; 248 | } 249 | } 250 | Type fieldType = i.FieldType; 251 | if (fieldType.IsPrimitive) 252 | { 253 | i.SetValue(t, ReadPrimitive(fieldType)); 254 | } 255 | else if (fieldType.IsEnum) 256 | { 257 | Type e = fieldType.GetField("value__").FieldType; 258 | i.SetValue(t, ReadPrimitive(e)); 259 | } 260 | else if (fieldType.IsArray) 261 | { 262 | ArrayLengthAttribute? arrayLengthAttribute = i.GetCustomAttribute(); 263 | if (!genericMethodCache.TryGetValue(fieldType, out MethodInfo? methodInfo)) 264 | { 265 | methodInfo = readClassArray.MakeGenericMethod(fieldType.GetElementType()); 266 | genericMethodCache.Add(fieldType, methodInfo); 267 | } 268 | i.SetValue(t, methodInfo.Invoke(this, new object[] { arrayLengthAttribute.Length })); 269 | } 270 | else 271 | { 272 | if (!genericMethodCache.TryGetValue(fieldType, out MethodInfo? methodInfo)) 273 | { 274 | methodInfo = readClass.MakeGenericMethod(fieldType); 275 | genericMethodCache.Add(fieldType, methodInfo); 276 | } 277 | i.SetValue(t, methodInfo.Invoke(this, null)); 278 | } 279 | } 280 | return t; 281 | } 282 | } 283 | 284 | public T[] ReadClassArray(long count) where T : new() 285 | { 286 | T[] t = new T[count]; 287 | for (int i = 0; i < count; i++) 288 | { 289 | t[i] = ReadClass(); 290 | } 291 | return t; 292 | } 293 | 294 | public T[] ReadClassArray(ulong addr, long count) where T : new() 295 | { 296 | Position = addr; 297 | return ReadClassArray(count); 298 | } 299 | 300 | public void WriteClass(ulong addr, T value) where T : new() 301 | { 302 | Position = addr; 303 | WriteClass(value); 304 | } 305 | 306 | public void WriteClass(T value) where T : new() 307 | { 308 | Type type = typeof(T); 309 | if (type.IsPrimitive) 310 | { 311 | WritePrimitive(type, value); 312 | } 313 | else 314 | { 315 | foreach (FieldInfo i in value.GetType().GetFields()) 316 | { 317 | if (!attributeCache.TryGetValue(i, out VersionAttribute[]? versionAttributes)) 318 | { 319 | if (Attribute.IsDefined(i, typeof(VersionAttribute))) 320 | { 321 | versionAttributes = i.GetCustomAttributes().ToArray(); 322 | attributeCache.Add(i, versionAttributes); 323 | } 324 | } 325 | if (versionAttributes?.Length > 0) 326 | { 327 | bool write = false; 328 | foreach (VersionAttribute versionAttribute in versionAttributes) 329 | { 330 | if (Version >= versionAttribute.Min && Version <= versionAttribute.Max) 331 | { 332 | write = true; 333 | break; 334 | } 335 | } 336 | if (!write) 337 | { 338 | continue; 339 | } 340 | } 341 | Type fieldType = i.FieldType; 342 | if (fieldType.IsPrimitive) 343 | { 344 | WritePrimitive(fieldType, i.GetValue(value)); 345 | } 346 | else if (fieldType.IsEnum) 347 | { 348 | Type e = fieldType.GetField("value__").FieldType; 349 | WritePrimitive(e, i.GetValue(value)); 350 | } 351 | else if (fieldType.IsArray) 352 | { 353 | ArrayLengthAttribute? arrayLengthAttribute = i.GetCustomAttribute(); 354 | if (!genericMethodCache.TryGetValue(fieldType, out MethodInfo? methodInfo)) 355 | { 356 | methodInfo = writeClassArray.MakeGenericMethod(fieldType.GetElementType()); 357 | genericMethodCache.Add(fieldType, methodInfo); 358 | } 359 | _ = methodInfo.Invoke(this, new object[] { arrayLengthAttribute.Length, i.GetValue(value) }); 360 | } 361 | else 362 | { 363 | if (!genericMethodCache.TryGetValue(fieldType, out MethodInfo? methodInfo)) 364 | { 365 | methodInfo = writeClass.MakeGenericMethod(fieldType); 366 | genericMethodCache.Add(fieldType, methodInfo); 367 | } 368 | _ = methodInfo.Invoke(this, new object[] { i.GetValue(value) }); 369 | } 370 | } 371 | } 372 | } 373 | 374 | public void WriteClassArray(long count, T[] values) where T : new() 375 | { 376 | for (int i = 0; i < count; i++) 377 | { 378 | T? value = values[i]; 379 | WriteClass(value); 380 | } 381 | } 382 | 383 | public void WriteClassArray(ulong addr, long count, T[] values) where T : new() 384 | { 385 | Position = addr; 386 | WriteClassArray(count, values); 387 | } 388 | 389 | protected virtual void Dispose(bool disposing) 390 | { 391 | if (disposing) 392 | { 393 | reader.Close(); 394 | writer.Close(); 395 | if (!_leaveOpen) 396 | { 397 | stream.Close(); 398 | } 399 | } 400 | } 401 | 402 | public void Dispose() 403 | { 404 | Dispose(true); 405 | } 406 | } -------------------------------------------------------------------------------- /Metadata/Utils/ConvertersUtils.cs: -------------------------------------------------------------------------------- 1 | using MetadataConverter2.IL2CPP; 2 | using static MetadataConverter2.IL2CPP.UnityIl2Cpp; 3 | 4 | namespace MetadataConverter2.Utils 5 | { 6 | public static class ConvertersUtils 7 | { 8 | public static StringLiteral StringLiteralConverter(MhyIl2Cpp.StringLiteral value) 9 | { 10 | return new() 11 | { 12 | length = value.length, 13 | dataIndex = value.dataIndex 14 | }; 15 | } 16 | 17 | public static PropertyDefinition PropertyDefinitionConverter(MhyIl2Cpp.PropertyDefinition value) 18 | { 19 | return new() 20 | { 21 | nameIndex = value.nameIndex, 22 | get = value.get, 23 | set = value.set, 24 | attrs = value.attrs, 25 | customAttributeIndex = value.customAttributeIndex, 26 | token = value.token 27 | }; 28 | } 29 | 30 | public static MethodDefinition MethodDefinitionConverter(MhyIl2Cpp.MethodDefinition value) 31 | { 32 | return new() 33 | { 34 | nameIndex = value.nameIndex, 35 | declaringType = value.declaringType, 36 | returnType = value.returnType, 37 | parameterStart = value.parameterStart, 38 | customAttributeIndex = value.customAttributeIndex, 39 | genericContainerIndex = value.genericContainerIndex, 40 | methodIndex = value.methodIndex, 41 | invokerIndex = value.invokerIndex, 42 | delegateWrapperIndex = value.delegateWrapperIndex, 43 | rgctxStartIndex = value.rgctxStartIndex, 44 | rgctxCount = value.rgctxCount, 45 | token = value.token, 46 | flags = value.flags, 47 | iflags = value.iflags, 48 | slot = value.slot, 49 | parameterCount = value.parameterCount 50 | }; 51 | } 52 | 53 | public static FieldDefinition FieldDefinitionConverter(MhyIl2Cpp.FieldDefinition value) 54 | { 55 | return new() 56 | { 57 | nameIndex = value.nameIndex, 58 | typeIndex = value.typeIndex, 59 | customAttributeIndex = value.customAttributeIndex, 60 | token = value.token 61 | }; 62 | } 63 | 64 | public static TypeDefinition TypeDefinitionConverter(MhyIl2Cpp.TypeDefinition value) 65 | { 66 | return new() 67 | { 68 | nameIndex = value.nameIndex, 69 | namespaceIndex = value.namespaceIndex, 70 | customAttributeIndex = value.customAttributeIndex, 71 | byvalTypeIndex = value.byvalTypeIndex, 72 | byrefTypeIndex = value.byrefTypeIndex, 73 | declaringTypeIndex = value.declaringTypeIndex, 74 | parentIndex = value.parentIndex, 75 | elementTypeIndex = value.elementTypeIndex, 76 | rgctxStartIndex = value.rgctxStartIndex, 77 | rgctxCount = value.rgctxCount, 78 | genericContainerIndex = value.genericContainerIndex, 79 | flags = value.flags, 80 | fieldStart = value.fieldStart, 81 | methodStart = value.methodStart, 82 | eventStart = value.eventStart, 83 | propertyStart = value.propertyStart, 84 | nestedTypesStart = value.nestedTypesStart, 85 | interfacesStart = value.interfacesStart, 86 | vtableStart = value.vtableStart, 87 | interfaceOffsetsStart = value.interfaceOffsetsStart, 88 | method_count = value.method_count, 89 | property_count = value.property_count, 90 | field_count = value.field_count, 91 | event_count = value.event_count, 92 | nested_type_count = value.nested_type_count, 93 | vtable_count = value.vtable_count, 94 | interfaces_count = value.interfaces_count, 95 | interface_offsets_count = value.interface_offsets_count, 96 | bitfield = value.bitfield, 97 | token = value.token 98 | }; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metadata 2 | A tool that decrypt/convert customized metadata files. 3 | _____________________________________________________________________________________________________________________________ 4 | How to use: 5 | ``` 6 | Metadata v1.0.0.0 7 | ------------------------ 8 | Usage: 9 | Metadata [convert] 10 | 11 | Arguments: 12 | Input metadata file. 13 | Output metadata file. 14 | (GI|GIPack|GICB1|GICBX|GIV2|BH3|SR|ZZZ) 15 | [convert] enable converting to standard unity format. 16 | ``` 17 | _____________________________________________________________________________________________________________________________ 18 | Looking forward for feedback for issues/bugs to fix and update. 19 | _____________________________________________________________________________________________________________________________ 20 | Special Thank to: 21 | - Dim 22 | - Perfare 23 | - PartyPooper 24 | - Ayy Lmao 25 | - Nitro 26 | - Hotaru 27 | - Samboy 28 | - Khang06 29 | _____________________________________________________________________________________________________________________________ 30 | 31 | If you find `Metadata` useful, you can leave a star 😄 32 | Thank you, looking forward for your feedback 33 | --------------------------------------------------------------------------------