#nullable enable
|
|
using IPA.Utilities;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
#if NET3
|
|
using Net3_Proxy;
|
|
using Path = Net3_Proxy.Path;
|
|
using File = Net3_Proxy.File;
|
|
using Directory = Net3_Proxy.Directory;
|
|
#endif
|
|
|
|
namespace IPA.Injector
|
|
{
|
|
internal static class GameVersionEarly
|
|
{
|
|
internal static string ResolveDataPath(string installDir) =>
|
|
Directory.EnumerateDirectories(installDir, "*_Data").First();
|
|
|
|
internal static string GlobalGameManagers(string installDir) =>
|
|
Path.Combine(ResolveDataPath(installDir), "globalgamemanagers");
|
|
|
|
internal static string GetGameVersion()
|
|
{
|
|
var mgr = GlobalGameManagers(UnityGame.InstallPath);
|
|
|
|
using (var stream = File.OpenRead(mgr))
|
|
using (var reader = new BinaryReader(stream, Encoding.UTF8))
|
|
{
|
|
const string key = "public.app-category.games";
|
|
int pos = 0;
|
|
var streamLength = stream.Length;
|
|
|
|
while (stream.Position < streamLength && pos < key.Length)
|
|
{
|
|
if (reader.ReadByte() == key[pos]) pos++;
|
|
else pos = 0;
|
|
}
|
|
|
|
if (stream.Position == streamLength) // we went through the entire stream without finding the key
|
|
throw new KeyNotFoundException("Could not find key '" + key + "' in " + mgr);
|
|
|
|
var startIndex = 0L;
|
|
var endIndex = 0L;
|
|
|
|
while (stream.Position < streamLength && endIndex == 0L)
|
|
{
|
|
var current = (char)reader.ReadByte();
|
|
if (char.IsDigit(current))
|
|
{
|
|
startIndex = stream.Position - 1;
|
|
var dotCount = 0;
|
|
|
|
while (stream.Position < streamLength)
|
|
{
|
|
current = (char)reader.ReadByte();
|
|
if (char.IsDigit(current))
|
|
{
|
|
if (dotCount == 2 && stream.Position < streamLength && !char.IsDigit((char)reader.PeekChar()))
|
|
{
|
|
endIndex = stream.Position;
|
|
break;
|
|
}
|
|
}
|
|
else if (current == '.')
|
|
{
|
|
dotCount++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var strlen = (int)(endIndex - startIndex);
|
|
_ = stream.Seek(-strlen, SeekOrigin.Current);
|
|
var strbytes = reader.ReadBytes(strlen);
|
|
|
|
return Encoding.UTF8.GetString(strbytes);
|
|
}
|
|
}
|
|
|
|
internal static AlmostVersion SafeParseVersion() => new(GetGameVersion());
|
|
|
|
private static void _Load()
|
|
{
|
|
UnityGame.SetEarlyGameVersion(SafeParseVersion());
|
|
UnityGame.CheckGameVersionBoundary();
|
|
}
|
|
|
|
internal static void Load()
|
|
{
|
|
// This exists for the same reason the wierdness in Injector.Main does
|
|
_ = Type.GetType("SemVer.Version, SemVer", false);
|
|
|
|
_Load();
|
|
}
|
|
|
|
internal static readonly char[] IllegalCharacters = new char[]
|
|
{
|
|
'<', '>', ':', '/', '\\', '|', '?', '*', '"',
|
|
'\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
|
|
'\u0008', '\u0009', '\u000a', '\u000b', '\u000c', '\u000d', '\u000e', '\u000d',
|
|
'\u000f', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016',
|
|
'\u0017', '\u0018', '\u0019', '\u001a', '\u001b', '\u001c', '\u001d', '\u001f',
|
|
};
|
|
}
|
|
}
|