Files
Il2CppInspectorRedux/Il2CppInspector.Common/Utils/BlobReader.cs
2024-08-13 18:34:22 +02:00

180 lines
6.7 KiB
C#

using NoisyCowStudios.Bin2Object;
using System.Text;
using System;
using System.Diagnostics;
using System.IO;
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Utils;
public static class BlobReader
{
public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppTypeEnum type, BinaryObjectStream blob)
{
const byte kArrayTypeWithDifferentElements = 1;
object value = null;
switch (type)
{
case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
value = blob.ReadBoolean();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U1:
case Il2CppTypeEnum.IL2CPP_TYPE_I1:
value = blob.ReadByte();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
// UTF-8 character assumed
value = BitConverter.ToChar(blob.ReadBytes(2), 0);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U2:
value = blob.ReadUInt16();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I2:
value = blob.ReadInt16();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U4:
value = ReadUInt32();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I4:
value = ReadInt32();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U8:
value = blob.ReadUInt64();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I8:
value = blob.ReadInt64();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_R4:
value = blob.ReadSingle();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_R8:
value = blob.ReadDouble();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
var uiLen = ReadInt32();
if (uiLen != -1)
value = Encoding.UTF8.GetString(blob.ReadBytes(uiLen));
break;
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
var length = ReadInt32();
if (length == -1)
break;
// This is only used in custom arguments.
// We actually want the reflection TypeInfo here, but as we do not have it yet
// we store everything in a custom array type to be changed out later in the TypeModel.
var arrayElementType = ReadEncodedTypeEnum(inspector, blob, out var arrayElementDef);
var arrayElementsAreDifferent = blob.ReadByte();
var array = new ConstantBlobArrayElement[length];
if (arrayElementsAreDifferent == kArrayTypeWithDifferentElements)
{
for (int i = 0; i < length; i++)
{
var elementType = ReadEncodedTypeEnum(inspector, blob, out var elementTypeDef);
array[i] = new ConstantBlobArrayElement(elementTypeDef, GetConstantValueFromBlob(inspector, elementType, blob), elementType);
}
}
else
{
for (int i = 0; i < length; i++)
{
array[i] = new ConstantBlobArrayElement(arrayElementDef, GetConstantValueFromBlob(inspector, arrayElementType, blob), arrayElementType);
}
}
value = new ConstantBlobArray(arrayElementDef, array, arrayElementType);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
break;
case Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX:
var index = blob.ReadCompressedInt32();
if (index != -1)
value = inspector.TypeReferences[index];
break;
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
break;
default:
Debugger.Break();
break;
}
return value;
int ReadInt32()
{
if (blob.Version >= MetadataVersions.V290)
{
var address = blob.Position;
try
{
return blob.ReadCompressedInt32();
}
catch (InvalidDataException)
{
Console.WriteLine($"Found invalid compressed int at metadata address 0x{address:x8}. Reading as normal int.");
return blob.ReadInt32(address);
}
}
return blob.ReadInt32();
}
uint ReadUInt32()
{
if (blob.Version >= MetadataVersions.V290)
{
var address = blob.Position;
try
{
return blob.ReadCompressedUInt32();
}
catch (InvalidDataException)
{
Console.WriteLine($"Found invalid compressed uint at metadata address 0x{address:x8}. Reading as normal uint.");
return blob.ReadUInt32(address);
}
}
return blob.ReadUInt32();
}
}
public static Il2CppTypeEnum ReadEncodedTypeEnum(Il2CppInspector inspector, BinaryObjectStream blob,
out Il2CppTypeDefinition enumType)
{
enumType = default;
var typeEnum = (Il2CppTypeEnum)blob.ReadByte();
if (typeEnum == Il2CppTypeEnum.IL2CPP_TYPE_ENUM)
{
var typeIndex = blob.ReadCompressedInt32();
var typeHandle = (uint)inspector.TypeReferences[typeIndex].Data.KlassIndex;
enumType = inspector.TypeDefinitions[typeHandle];
var elementTypeHandle = inspector.TypeReferences[enumType.ElementTypeIndex].Data.KlassIndex;
var elementType = inspector.TypeDefinitions[elementTypeHandle];
typeEnum = inspector.TypeReferences[elementType.ByValTypeIndex].Type;
}
// This technically also handles SZARRAY (System.Array) and all others by just returning their system type
return typeEnum;
}
public record ConstantBlobArray(Il2CppTypeDefinition ArrayTypeDef, ConstantBlobArrayElement[] Elements, Il2CppTypeEnum ArrayTypeEnum);
public record ConstantBlobArrayElement(Il2CppTypeDefinition TypeDef, object Value, Il2CppTypeEnum TypeEnum);
}