From 0b2947e72cbcb03ddac9f4d06ba59ad6150e6408 Mon Sep 17 00:00:00 2001 From: 321Proteus Date: Sun, 18 Jan 2026 19:31:55 +0100 Subject: [PATCH 1/4] Added fallback pCodegenModules search --- LibCpp2IL/BinarySearcher.cs | 64 +++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index bd3998f3..4090cdca 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -169,34 +169,58 @@ internal ulong FindCodeRegistrationPost2019(Il2CppMetadata metadata) pSomewhereInCodegenModules = pSomewhereInCodegenModules.Select(va => va - ptrSize * (ulong)initialBacktrack); //Slightly experimental, but we're gonna try backtracking most of the way through the number of modules. Not all the way because we don't want to overshoot. - int backtrack; - for (backtrack = initialBacktrack; backtrack < sanityCheckNumberOfModules && (pCodegenModules?.Count() ?? 0) != 1; backtrack++) - { - pCodegenModules = FindAllMappedWords(pSomewhereInCodegenModules).ToList(); - //Sanity check the count, which is one pointer back - if (pCodegenModules.Count == 1) + List FindCodegenModules(int initialBacktrackParam) + { + List foundModules = []; + + for (int backtrack = initialBacktrackParam; backtrack < sanityCheckNumberOfModules && foundModules.Count != 1; backtrack++) { - binary.Reader.Position = binary.MapVirtualAddressToRaw(pCodegenModules.First() - ptrSize); - var moduleCount = binary.Reader.ReadInt32(); + foundModules = FindAllMappedWords(pSomewhereInCodegenModules).ToList(); - if (moduleCount < 0 || moduleCount > sanityCheckNumberOfModules) - pCodegenModules = []; - else - LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}, module count is {LibCpp2IlMain.TheMetadata!.imageDefinitions.Length}"); + //Sanity check the count, which is one pointer back + if (foundModules.Count == 1) + { + if (foundModules.Any()) + { + binary.Reader.Position = binary.MapVirtualAddressToRaw(foundModules.First() - ptrSize); + var moduleCount = binary.Reader.ReadInt32(); + + if (moduleCount < 0 || moduleCount > sanityCheckNumberOfModules) + foundModules = []; + else + LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}, module count is {LibCpp2IlMain.TheMetadata!.imageDefinitions.Length}"); + } + } + else if (foundModules.Count > 1) + { + LibLogger.VerboseNewline($"\t\t\tFound {foundModules.Count} potential pCodegenModules addresses after a backtrack of {backtrack}, which is too many (> 1). Will try backtracking further."); + } + pSomewhereInCodegenModules = pSomewhereInCodegenModules.Select(va => va - ptrSize); } - else if (pCodegenModules.Count > 1) + + return foundModules; + } + + var backtrackedModules = FindCodegenModules(initialBacktrack); + if (backtrackedModules.Count < 1) + { + LibLogger.WarnNewline($"Hit backtrack limit of {sanityCheckNumberOfModules} modules and still didn't find a valid pCodegenModules pointer. Switching to the fallback non-backtracked search"); + pSomewhereInCodegenModules = pMscorlibCodegenEntryInCodegenModulesList.AsEnumerable(); + var unbacktrackedModules = FindCodegenModules(0); + if (unbacktrackedModules.Count < 1) { - LibLogger.VerboseNewline($"\t\t\tFound {pCodegenModules.Count} potential pCodegenModules addresses after a backtrack of {backtrack}, which is too many (> 1). Will try backtracking further."); + LibLogger.WarnNewline("Fallback search failed to find a valid pCodegen modules pointer."); + return 0; + } + else + { + pCodegenModules = unbacktrackedModules; } - - pSomewhereInCodegenModules = pSomewhereInCodegenModules.Select(va => va - ptrSize); } - - if (backtrack == sanityCheckNumberOfModules && (pCodegenModules?.Count() ?? 0) != 1) + else { - LibLogger.WarnNewline($"Hit backtrack limit of {backtrack} modules and still didn't find a valid pCodegenModules pointer."); - return 0; + pCodegenModules = backtrackedModules; } if (pCodegenModules?.Any() != true) From 4176de1db00c8afdfdcb85c8b870579acf9b5c7f Mon Sep 17 00:00:00 2001 From: 321Proteus Date: Mon, 19 Jan 2026 16:13:29 +0100 Subject: [PATCH 2/4] Rephrased verbose logging for backtrack --- LibCpp2IL/BinarySearcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 4090cdca..f01e451d 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -189,7 +189,7 @@ List FindCodegenModules(int initialBacktrackParam) if (moduleCount < 0 || moduleCount > sanityCheckNumberOfModules) foundModules = []; else - LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}, module count is {LibCpp2IlMain.TheMetadata!.imageDefinitions.Length}"); + LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {backtrack}/{LibCpp2IlMain.TheMetadata!.imageDefinitions.Length}: {foundModules[0]:X}"); } } else if (foundModules.Count > 1) From a42c7fb31ae426701783366961e2a54615436277 Mon Sep 17 00:00:00 2001 From: 321Proteus Date: Mon, 19 Jan 2026 16:47:02 +0100 Subject: [PATCH 3/4] Allow multiple output formats --- Cpp2IL.Core/Cpp2IlRuntimeArgs.cs | 2 +- Cpp2IL/CommandLineArgs.cs | 3 +-- Cpp2IL/Program.cs | 33 ++++++++++++++++++++------------ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IlRuntimeArgs.cs b/Cpp2IL.Core/Cpp2IlRuntimeArgs.cs index e1c336ef..dba49879 100644 --- a/Cpp2IL.Core/Cpp2IlRuntimeArgs.cs +++ b/Cpp2IL.Core/Cpp2IlRuntimeArgs.cs @@ -19,7 +19,7 @@ public class Cpp2IlRuntimeArgs public List ProcessingLayersToRun = []; public readonly Dictionary ProcessingLayerConfigurationOptions = new(); - public Cpp2IlOutputFormat? OutputFormat; + public IEnumerable? OutputFormats; public string OutputRootDirectory = null!; public bool LowMemoryMode; diff --git a/Cpp2IL/CommandLineArgs.cs b/Cpp2IL/CommandLineArgs.cs index bd3f8e80..b2f4cb6e 100644 --- a/Cpp2IL/CommandLineArgs.cs +++ b/Cpp2IL/CommandLineArgs.cs @@ -43,9 +43,8 @@ public class CommandLineArgs [Option("list-output-formats", HelpText = "List the available output formats and exit.")] public bool ListOutputFormats { get; set; } - //FUTURE: Allow multiple of these? [Option("output-as", HelpText = "Specify the ID of the output format you wish to use.")] - public string? OutputFormatId { get; set; } + public IEnumerable OutputFormatIds { get; set; } = new List(); [Option("output-to", HelpText = "Root directory to output to. Defaults to cpp2il_out in the current working directory.")] public string OutputRootDir { get; set; } = Path.GetFullPath("cpp2il_out"); diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 97396db8..49769072 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -482,8 +482,9 @@ private static void HandleIpa(string gamePath, ref Cpp2IlRuntimeArgs args) [DynamicDependency(DynamicallyAccessedMemberTypes.All, "Cpp2IL.CommandLineArgs", "Cpp2IL")] #endif private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] commandLine) - { - var parserResult = Parser.Default.ParseArguments(commandLine); + { + var parserResult = new Parser(settings => settings.AllowMultiInstance = true) + .ParseArguments(commandLine); if (parserResult is NotParsed notParsed && notParsed.Errors.Count() == 1 && notParsed.Errors.All(e => e.Tag is ErrorType.VersionRequestedError or ErrorType.HelpRequestedError)) //Version or help requested @@ -555,12 +556,12 @@ private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] comma // if(string.IsNullOrEmpty(options.OutputFormatId)) // throw new SoftException("No output format specified, so nothing to do!"); - if (!string.IsNullOrEmpty(options.OutputFormatId)) + if (options.OutputFormatIds.Any() == true) { try { - result.OutputFormat = OutputFormatRegistry.GetFormat(options.OutputFormatId!); - Logger.VerboseNewline($"Selected output format: {result.OutputFormat.OutputFormatName}"); + result.OutputFormats = options.OutputFormatIds.Select(OutputFormatRegistry.GetFormat).ToList(); + Logger.VerboseNewline($"Selected output formats: [{string.Join(", ", options.OutputFormatIds)}]"); } catch (Exception e) { @@ -647,7 +648,13 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) var executionStart = DateTime.Now; - runtimeArgs.OutputFormat?.OnOutputFormatSelected(); + if (runtimeArgs.OutputFormats != null) + { + foreach(Cpp2IlOutputFormat format in runtimeArgs.OutputFormats) + { + format.OnOutputFormatSelected(); + } + } GCSettings.LatencyMode = runtimeArgs.LowMemoryMode ? GCLatencyMode.Interactive : GCLatencyMode.SustainedLowLatency; @@ -687,14 +694,16 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) var outputStart = DateTime.Now; - if (runtimeArgs.OutputFormat != null) + if (runtimeArgs.OutputFormats != null) { - if (runtimeArgs.LowMemoryMode) + foreach (Cpp2IlOutputFormat format in runtimeArgs.OutputFormats) + { + if (runtimeArgs.LowMemoryMode) GC.Collect(); - - Logger.InfoNewline($"Outputting as {runtimeArgs.OutputFormat.OutputFormatName} to {runtimeArgs.OutputRootDirectory}..."); - runtimeArgs.OutputFormat.DoOutput(Cpp2IlApi.CurrentAppContext, runtimeArgs.OutputRootDirectory); - Logger.InfoNewline($"Finished outputting in {(DateTime.Now - outputStart).TotalMilliseconds}ms"); + Logger.InfoNewline($"Outputting as {format.OutputFormatName} to {runtimeArgs.OutputRootDirectory}..."); + format.DoOutput(Cpp2IlApi.CurrentAppContext, runtimeArgs.OutputRootDirectory); + Logger.InfoNewline($"Finished outputting in {(DateTime.Now - outputStart).TotalMilliseconds}ms"); + } } else { From 25d226b9ce835a3dc4e0ecf52d292c67ba956c0e Mon Sep 17 00:00:00 2001 From: 321Proteus Date: Tue, 20 Jan 2026 17:24:33 +0100 Subject: [PATCH 4/4] Fixed help and version in new parser --- Cpp2IL/Program.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 49769072..2c6eff40 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -482,8 +482,12 @@ private static void HandleIpa(string gamePath, ref Cpp2IlRuntimeArgs args) [DynamicDependency(DynamicallyAccessedMemberTypes.All, "Cpp2IL.CommandLineArgs", "Cpp2IL")] #endif private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] commandLine) - { - var parserResult = new Parser(settings => settings.AllowMultiInstance = true) + { + var parserResult = new Parser(settings => + { + settings.AllowMultiInstance = true; + settings.HelpWriter = Console.Out; + }) .ParseArguments(commandLine); if (parserResult is NotParsed notParsed && notParsed.Errors.Count() == 1 && notParsed.Errors.All(e => e.Tag is ErrorType.VersionRequestedError or ErrorType.HelpRequestedError)) @@ -531,7 +535,7 @@ private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] comma if (options.GamePath != null && options.GamePath.StartsWith("~")) options.GamePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + options.GamePath[1..]; #endif - + ResolvePathsFromCommandLine(options.GamePath, options.ExeName, ref result); } else @@ -650,10 +654,10 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) if (runtimeArgs.OutputFormats != null) { - foreach(Cpp2IlOutputFormat format in runtimeArgs.OutputFormats) + foreach (Cpp2IlOutputFormat format in runtimeArgs.OutputFormats) { - format.OnOutputFormatSelected(); - } + format.OnOutputFormatSelected(); + } } GCSettings.LatencyMode = runtimeArgs.LowMemoryMode ? GCLatencyMode.Interactive : GCLatencyMode.SustainedLowLatency; @@ -699,10 +703,10 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) foreach (Cpp2IlOutputFormat format in runtimeArgs.OutputFormats) { if (runtimeArgs.LowMemoryMode) - GC.Collect(); + GC.Collect(); Logger.InfoNewline($"Outputting as {format.OutputFormatName} to {runtimeArgs.OutputRootDirectory}..."); format.DoOutput(Cpp2IlApi.CurrentAppContext, runtimeArgs.OutputRootDirectory); - Logger.InfoNewline($"Finished outputting in {(DateTime.Now - outputStart).TotalMilliseconds}ms"); + Logger.InfoNewline($"Finished outputting in {(DateTime.Now - outputStart).TotalMilliseconds}ms"); } } else