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