diff --git a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs index 05ef5e26..8d613a3b 100644 --- a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace LibCpp2IL.Metadata; @@ -28,8 +30,100 @@ public byte[]? PublicKey { get { - var result = LibCpp2IlMain.TheMetadata!.GetByteArrayFromIndex(publicKeyIndex); - return result.Length == 0 ? null : result; + // The difference between these two options can be seen in the header files. + // const uint8_t* public_key; + // const char* public_key; + + if (IsAtLeast(24.4f) || Is(24.15f)) + { + // 2018.4.34 until 2019 (24.15) + // 2019.4.15 until 2020 (24.4) + // 2020.1.11 until 2020.2.0 (24.4) + // 2020.2.0b7 and later (27+) + var result = LibCpp2IlMain.TheMetadata!.GetByteArrayFromIndex(publicKeyIndex); + return result.Length == 0 ? null : result; + } + else + { + var str = LibCpp2IlMain.TheMetadata!.GetStringFromIndex(publicKeyIndex); + + if (str is "NULL") + return null; // No public key + + // mscorlib example: + // "\x0\x0\x0\x0\x0\x0\x0\x0\x4\x0\x0\x0\x0\x0\x0\x0" + // The string above is verbatim, so the backslashes are not escape characters and the quotes are part of the string. + if (str.Length > 2 && str[0] is '"' && str[^1] is '"' && TryParseByteArray(str.AsSpan()[1..^1], out var result)) + return result; + + return null; // Parsing failed + } + + static bool IsHexDigit(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f'); + + static bool TryParseByte(ReadOnlySpan characters, out byte result) + { +#if NET6_0_OR_GREATER + return byte.TryParse(characters, System.Globalization.NumberStyles.HexNumber, null, out result); +#else + return byte.TryParse(characters.ToString(), System.Globalization.NumberStyles.HexNumber, null, out result); +#endif + } + + static bool TryParseByteArray(ReadOnlySpan characters, [NotNullWhen(true)] out byte[]? result) + { + var bytes = new List(); + + var i = 0; + while (i < characters.Length) + { + // Each byte is represented by 3 or 4 characters. + // The first one is backslash. + // The second one is 'x'. + // The third and fourth (if present) ones are the hexadecimal representation of the byte. + if (i > characters.Length - 3 || characters[i] is not '\\' || characters[i + 1] is not 'x') + { + result = null; + return false; + } + + i += 2; + + if (!IsHexDigit(characters[i])) + { + result = null; + return false; + } + + ReadOnlySpan hexCharacters; + if (i + 1 < characters.Length && characters[i + 1] is not '\\') + { + if (!IsHexDigit(characters[i + 1])) + { + result = null; + return false; + } + hexCharacters = characters.Slice(i, 2); + i += 2; + } + else + { + hexCharacters = characters.Slice(i, 1); + i += 1; + } + + if (!TryParseByte(hexCharacters, out var b)) + { + result = null; + return false; + } + + bytes.Add(b); + } + + result = bytes.ToArray(); + return true; + } } } diff --git a/LibCpp2IL/ReadableClass.cs b/LibCpp2IL/ReadableClass.cs index c894a850..fadb1bab 100644 --- a/LibCpp2IL/ReadableClass.cs +++ b/LibCpp2IL/ReadableClass.cs @@ -1,13 +1,14 @@ -using System; +using System; namespace LibCpp2IL; public abstract class ReadableClass { - protected bool IsAtLeast(float vers) => LibCpp2IlMain.MetadataVersion >= vers; - protected bool IsLessThan(float vers) => LibCpp2IlMain.MetadataVersion < vers; - protected bool IsAtMost(float vers) => LibCpp2IlMain.MetadataVersion <= vers; - protected bool IsNot(float vers) => Math.Abs(LibCpp2IlMain.MetadataVersion - vers) > 0.001f; + protected static bool IsAtLeast(float vers) => LibCpp2IlMain.MetadataVersion >= vers; + protected static bool IsLessThan(float vers) => LibCpp2IlMain.MetadataVersion < vers; + protected static bool IsAtMost(float vers) => LibCpp2IlMain.MetadataVersion <= vers; + protected static bool IsNot(float vers) => Math.Abs(LibCpp2IlMain.MetadataVersion - vers) > 0.001f; + protected static bool Is(float vers) => Math.Abs(LibCpp2IlMain.MetadataVersion - vers) < 0.001f; public abstract void Read(ClassReadingBinaryReader reader); }