Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cpp2IL/Cpp2IL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<TrimMode>partial</TrimMode>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">
<TrimmerRootAssembly Include="System.Runtime" />
<TrimmerRootAssembly Include="System.Private.CoreLib" />
Expand All @@ -31,6 +31,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.2" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Pastel" Version="6.0.1" />
<PackageReference Include="PolySharp" Version="1.15.0">
Expand Down
93 changes: 91 additions & 2 deletions Cpp2IL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Text;
using System.Text.Json;
using CommandLine;
using Cpp2IL.Core;
using Cpp2IL.Core.Api;
Expand Down Expand Up @@ -35,7 +37,7 @@
{
if (string.IsNullOrEmpty(gamePath))
throw new SoftException("No force options provided, and no game path was provided either. Please provide a game path or use the --force- options.");

//Somehow the above doesn't tell .net that gamePath can't be null on net472, so we do this stupid thing to avoid nullable warnings
#if NET472
gamePath = gamePath!;
Expand All @@ -44,17 +46,19 @@
Logger.VerboseNewline("Beginning path resolution...");

if (Directory.Exists(gamePath) && File.Exists(Path.Combine(gamePath, "Contents/Frameworks/GameAssembly.dylib")))
HandleMacOSGamePath(gamePath, inputExeName, ref args);

Check warning on line 49 in Cpp2IL/Program.cs

View workflow job for this annotation

GitHub Actions / Build - Windows .NET Framework Zip

Possible null reference argument for parameter 'gamePath' in 'void Program.HandleMacOSGamePath(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args)'.
else if (Directory.Exists(gamePath) && File.Exists(Path.Combine(gamePath, "GameAssembly.so")))
HandleLinuxGamePath(gamePath, inputExeName, ref args);

Check warning on line 51 in Cpp2IL/Program.cs

View workflow job for this annotation

GitHub Actions / Build - Windows .NET Framework Zip

Possible null reference argument for parameter 'gamePath' in 'void Program.HandleLinuxGamePath(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args)'.
else if (Directory.Exists(gamePath))
HandleWindowsGamePath(gamePath, inputExeName, ref args);

Check warning on line 53 in Cpp2IL/Program.cs

View workflow job for this annotation

GitHub Actions / Build - Windows .NET Framework Zip

Possible null reference argument for parameter 'gamePath' in 'void Program.HandleWindowsGamePath(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args)'.
else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".apk")
HandleSingleApk(gamePath, ref args);

Check warning on line 55 in Cpp2IL/Program.cs

View workflow job for this annotation

GitHub Actions / Build - Windows .NET Framework Zip

Possible null reference argument for parameter 'gamePath' in 'void Program.HandleSingleApk(string gamePath, ref Cpp2IlRuntimeArgs args)'.
else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() is ".xapk" or ".apkm")
HandleXapk(gamePath, ref args);

Check warning on line 57 in Cpp2IL/Program.cs

View workflow job for this annotation

GitHub Actions / Build - Windows .NET Framework Zip

Possible null reference argument for parameter 'gamePath' in 'void Program.HandleXapk(string gamePath, ref Cpp2IlRuntimeArgs args)'.
else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() is ".ipa" or ".tipa")
HandleIpa(gamePath, ref args);
else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".json")
HandleWebGL(gamePath, ref args);
else
{
if (!Cpp2IlPluginManager.TryProcessGamePath(gamePath, ref args))
Expand Down Expand Up @@ -487,6 +491,91 @@
args.Valid = true;
}

private static void HandleWebGL(string gamePath, ref Cpp2IlRuntimeArgs args)
{
Logger.VerboseNewline("Trying HandleWebGL as provided path is an json file");

Logger.InfoNewline($"Attempting to extract required file paths from WebGL json config {gamePath}", "WebGL");

var json = File.ReadAllText(gamePath);
var doc = JsonDocument.Parse(json) ?? throw new SoftException("WebGL config is null.");
var frameworkUrl = doc.RootElement.GetProperty("wasmFrameworkUrl").GetString() ?? throw new SoftException("wasmFrameworkUrl is null.");
var codeUrl = doc.RootElement.GetProperty("wasmCodeUrl").GetString() ?? throw new SoftException("wasmCodeUrl is null.");
var dataUrl = doc.RootElement.GetProperty("dataUrl").GetString() ?? throw new SoftException("dataUrl is null.");

var tempFileMeta = Path.GetTempFileName();
PathsToDeleteOnExit.Add(tempFileMeta);

var gameDir = Path.GetDirectoryName(gamePath) ?? "";
args.PathToAssembly = Path.Combine(gameDir, codeUrl);
args.WasmFrameworkJsFile = Path.Combine(gameDir, frameworkUrl);
args.PathToMetadata = tempFileMeta;

var dataBytes = File.ReadAllBytes(Path.Combine(gameDir, dataUrl));
var dataFiles = ParseUnityWebData(dataBytes);

if (dataFiles.TryGetValue("globalgamemanagers", out var globalGameManagers))
{
Logger.InfoNewline("Reading globalgamemanagers to determine unity version...", "WebGL");
var ggmBytes = new byte[0x40];
using var ggmStream = new MemoryStream(globalGameManagers);

// ReSharper disable once MustUseReturnValue
ggmStream.Read(ggmBytes, 0, 0x40);

args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(ggmBytes);
}
else if (dataFiles.TryGetValue("data.unity3d", out var dataUnity3d))
{
Logger.InfoNewline("Reading data.unity3d to determine unity version...", "WebGL");
using var du3dStream = new MemoryStream(dataUnity3d);

args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(du3dStream);
}
else
throw new SoftException("Could not find globalgamemanagers or unity3d inside UnityWebData.");

if (!dataFiles.TryGetValue("Il2CppData/Metadata/global-metadata.dat", out var globalMetadata))
throw new SoftException("Could not find global-metadata.dat inside UnityWebData.");
File.WriteAllBytes(tempFileMeta, globalMetadata);

Logger.InfoNewline($"Determined game's unity version to be {args.UnityVersion}", "WebGL");

args.Valid = true;
}

private static Dictionary<string, byte[]> ParseUnityWebData(byte[] bytes)
{
const string HEADER = "UnityWebData1.0\0";

var files = new Dictionary<string, byte[]>();
var offset = 0;

var header = Encoding.ASCII.GetString(bytes, offset, HEADER.Length);
if (header != HEADER)
throw new SoftException($"Invalid UnityWebData header \"{header}\".");
offset += HEADER.Length;

var headerEnd = BitConverter.ToUInt32(bytes, offset);
offset += 4;

while (offset < headerEnd)
{
var fileOffset = BitConverter.ToUInt32(bytes, offset); offset += 4;
var fileSize = BitConverter.ToUInt32(bytes, offset); offset += 4;
var filePathLength = BitConverter.ToUInt32(bytes, offset); offset += 4;

var filePath = Encoding.ASCII.GetString(bytes, offset, (int)filePathLength);
offset += (int)filePathLength;

var fileData = new byte[fileSize];
Array.Copy(bytes, fileOffset, fileData, 0, fileData.Length);
files[filePath] = fileData;
}

return files;
}

#if !NETFRAMEWORK
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "Cpp2IL.CommandLineArgs", "Cpp2IL")]
#endif
Expand Down Expand Up @@ -539,7 +628,7 @@
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
Expand Down
Loading