mirror of
https://github.com/AssetRipper/AssetRipper.git
synced 2025-12-11 20:15:29 +01:00
882 lines
40 KiB
C#
882 lines
40 KiB
C#
using AssetRipper.AssemblyDumper.Attributes;
|
|
using AssetRipper.AssemblyDumper.InjectedTypes;
|
|
using AssetRipper.AssemblyDumper.Methods;
|
|
using AssetRipper.AssemblyDumper.Types;
|
|
using AssetRipper.Assets;
|
|
using AssetRipper.Assets.Generics;
|
|
using AssetRipper.IO.Endian;
|
|
using System.Diagnostics;
|
|
|
|
namespace AssetRipper.AssemblyDumper.Passes;
|
|
|
|
public static class Pass100_FillReadMethods
|
|
{
|
|
#nullable disable
|
|
private static IMethodDefOrRef alignStreamMethod;
|
|
private static IMethodDefOrRef readInt32Method;
|
|
private static IMethodDefOrRef readBytesMethod;
|
|
/// <summary>
|
|
/// TypeSignature for <see langword="ref"/> <see cref="EndianSpanReader"/>
|
|
/// </summary>
|
|
private static TypeSignature endianSpanReaderReference;
|
|
|
|
private static ITypeDefOrRef assetDictionaryReference;
|
|
private static TypeDefinition assetDictionaryDefinition;
|
|
private static ITypeDefOrRef assetListReference;
|
|
private static TypeDefinition assetListDefinition;
|
|
private static ITypeDefOrRef assetPairReference;
|
|
private static TypeDefinition assetPairDefinition;
|
|
|
|
private static MethodDefinition readAssetAlignDefinition;
|
|
private static MethodDefinition readAssetListDefinition;
|
|
private static MethodDefinition readAssetListAlignDefinition;
|
|
private static MethodDefinition readAssetPairDefinition;
|
|
private static MethodDefinition readAssetPairAlignDefinition;
|
|
private static MethodDefinition readAssetDictionaryDefinition;
|
|
private static MethodDefinition readAssetDictionaryAlignDefinition;
|
|
#nullable enable
|
|
|
|
private static readonly Dictionary<ElementType, IMethodDefOrRef> primitiveReadMethods = new();
|
|
|
|
private static string ReadMethod => emittingRelease ? ReadRelease : ReadEditor;
|
|
private const string ReadRelease = nameof(UnityAssetBase.ReadRelease);
|
|
private const string ReadEditor = nameof(UnityAssetBase.ReadEditor);
|
|
private const int MaxArraySize = 1024;
|
|
|
|
private static readonly Dictionary<string, IMethodDescriptor> methodDictionary = new();
|
|
private static readonly SignatureComparer signatureComparer = new(SignatureComparisonFlags.VersionAgnostic);
|
|
private static bool emittingRelease;
|
|
|
|
public static void DoPass()
|
|
{
|
|
methodDictionary.Clear();
|
|
Initialize();
|
|
|
|
emittingRelease = true;
|
|
readAssetAlignDefinition = MakeGenericAssetAlignMethod();
|
|
readAssetListDefinition = MakeGenericListMethod(false);
|
|
readAssetListAlignDefinition = MakeGenericListMethod(true);
|
|
readAssetPairDefinition = MakeGenericPairMethod(false);
|
|
readAssetPairAlignDefinition = MakeGenericPairMethod(true);
|
|
readAssetDictionaryDefinition = MakeGenericDictionaryMethod(false);
|
|
readAssetDictionaryAlignDefinition = MakeGenericDictionaryMethod(true);
|
|
foreach (ClassGroupBase group in SharedState.Instance.AllGroups)
|
|
{
|
|
foreach (GeneratedClassInstance instance in group.Instances)
|
|
{
|
|
instance.Type.FillReleaseWriteMethod(instance.Class, instance.VersionRange.Start);
|
|
}
|
|
}
|
|
CreateHelperClassForWriteMethods();
|
|
methodDictionary.Clear();
|
|
|
|
emittingRelease = false;
|
|
readAssetAlignDefinition = MakeGenericAssetAlignMethod();
|
|
readAssetListDefinition = MakeGenericListMethod(false);
|
|
readAssetListAlignDefinition = MakeGenericListMethod(true);
|
|
readAssetPairDefinition = MakeGenericPairMethod(false);
|
|
readAssetPairAlignDefinition = MakeGenericPairMethod(true);
|
|
readAssetDictionaryDefinition = MakeGenericDictionaryMethod(false);
|
|
readAssetDictionaryAlignDefinition = MakeGenericDictionaryMethod(true);
|
|
foreach (ClassGroupBase group in SharedState.Instance.AllGroups)
|
|
{
|
|
foreach (GeneratedClassInstance instance in group.Instances)
|
|
{
|
|
instance.Type.FillEditorWriteMethod(instance.Class, instance.VersionRange.Start);
|
|
}
|
|
}
|
|
CreateHelperClassForWriteMethods();
|
|
methodDictionary.Clear();
|
|
}
|
|
|
|
private static void Initialize()
|
|
{
|
|
primitiveReadMethods.Add(ElementType.Boolean, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadBoolean)));
|
|
primitiveReadMethods.Add(ElementType.Char, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadChar)));
|
|
primitiveReadMethods.Add(ElementType.I1, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadSByte)));
|
|
primitiveReadMethods.Add(ElementType.U1, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadByte)));
|
|
primitiveReadMethods.Add(ElementType.I2, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadInt16)));
|
|
primitiveReadMethods.Add(ElementType.U2, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadUInt16)));
|
|
primitiveReadMethods.Add(ElementType.I4, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadInt32)));
|
|
primitiveReadMethods.Add(ElementType.U4, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadUInt32)));
|
|
primitiveReadMethods.Add(ElementType.I8, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadInt64)));
|
|
primitiveReadMethods.Add(ElementType.U8, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadUInt64)));
|
|
primitiveReadMethods.Add(ElementType.R4, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadSingle)));
|
|
primitiveReadMethods.Add(ElementType.R8, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadDouble)));
|
|
primitiveReadMethods.Add(ElementType.String, SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.ReadUtf8String)));
|
|
|
|
readInt32Method = primitiveReadMethods[ElementType.I4];
|
|
|
|
alignStreamMethod = SharedState.Instance.Importer.ImportMethod(typeof(EndianSpanReader), m => m.Name == nameof(EndianSpanReader.Align));
|
|
endianSpanReaderReference = SharedState.Instance.Importer.ImportTypeSignature(typeof(EndianSpanReader)).MakeByReferenceType();
|
|
|
|
readBytesMethod = SharedState.Instance.InjectHelperType(typeof(TypelessDataHelper)).Methods.Single();
|
|
|
|
assetDictionaryReference = SharedState.Instance.Importer.ImportType(typeof(AssetDictionary<,>));
|
|
assetListReference = SharedState.Instance.Importer.ImportType(typeof(AssetList<>));
|
|
assetPairReference = SharedState.Instance.Importer.ImportType(typeof(AssetPair<,>));
|
|
|
|
assetDictionaryDefinition = SharedState.Instance.Importer.LookupType(typeof(AssetDictionary<,>));
|
|
assetListDefinition = SharedState.Instance.Importer.LookupType(typeof(AssetList<>));
|
|
assetPairDefinition = SharedState.Instance.Importer.LookupType(typeof(AssetPair<,>));
|
|
}
|
|
|
|
private static void CreateHelperClassForWriteMethods()
|
|
{
|
|
TypeDefinition type = StaticClassCreator.CreateEmptyStaticClass(SharedState.Instance.Module, SharedState.HelpersNamespace, $"{ReadMethod}Methods");
|
|
type.IsPublic = false;
|
|
type.Methods.Add(readAssetAlignDefinition);
|
|
type.Methods.Add(readAssetListDefinition);
|
|
type.Methods.Add(readAssetListAlignDefinition);
|
|
type.Methods.Add(readAssetPairDefinition);
|
|
type.Methods.Add(readAssetPairAlignDefinition);
|
|
type.Methods.Add(readAssetDictionaryDefinition);
|
|
type.Methods.Add(readAssetDictionaryAlignDefinition);
|
|
foreach ((string _, IMethodDescriptor method) in methodDictionary.OrderBy(pair => pair.Key))
|
|
{
|
|
if (method is MethodDefinition methodDefinition && methodDefinition.DeclaringType is null)
|
|
{
|
|
type.Methods.Add(methodDefinition);
|
|
}
|
|
}
|
|
Console.WriteLine($"\t{type.Methods.Count} {ReadMethod} helper methods");
|
|
}
|
|
|
|
private static void FillEditorWriteMethod(this TypeDefinition type, UniversalClass klass, UnityVersion version)
|
|
{
|
|
type.FillMethod(ReadEditor, klass.EditorRootNode, version);
|
|
}
|
|
|
|
private static void FillReleaseWriteMethod(this TypeDefinition type, UniversalClass klass, UnityVersion version)
|
|
{
|
|
type.FillMethod(ReadRelease, klass.ReleaseRootNode, version);
|
|
}
|
|
|
|
private static void FillMethod(this TypeDefinition type, string methodName, UniversalNode? rootNode, UnityVersion version)
|
|
{
|
|
MethodDefinition method = type.Methods.First(m => m.Name == methodName);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
if (rootNode is not null)
|
|
{
|
|
foreach (UniversalNode unityNode in rootNode.SubNodes)
|
|
{
|
|
FieldDefinition field = type.GetFieldByName(unityNode.Name, true);
|
|
IMethodDescriptor fieldReadMethod = GetOrMakeMethod(unityNode, field.Signature!.FieldType, version);
|
|
if (field.Signature.FieldType.IsArrayOrPrimitive())
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_0);//this
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(fieldReadMethod);
|
|
instructions.Add(CilOpCodes.Stfld, field);
|
|
}
|
|
else
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_0);//this
|
|
instructions.Add(CilOpCodes.Ldfld, field);
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(fieldReadMethod);
|
|
}
|
|
}
|
|
}
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
}
|
|
|
|
private static IMethodDescriptor GetOrMakeMethod(UniversalNode node, TypeSignature type, UnityVersion version)
|
|
{
|
|
string uniqueName = UniqueNameFactory.GetReadWriteName(node, version);
|
|
if (methodDictionary.TryGetValue(uniqueName, out IMethodDescriptor? method))
|
|
{
|
|
return method;
|
|
}
|
|
|
|
if (SharedState.Instance.SubclassGroups.TryGetValue(node.TypeName, out SubclassGroup? subclassGroup))
|
|
{
|
|
TypeDefinition typeDefinition = subclassGroup.GetTypeForVersion(version);
|
|
Debug.Assert(signatureComparer.Equals(typeDefinition.ToTypeSignature(), type));
|
|
MethodDefinition typeReadMethod = typeDefinition.GetMethodByName(ReadMethod);
|
|
method = node.AlignBytes ? readAssetAlignDefinition.MakeGenericInstanceMethod(type) : typeReadMethod;
|
|
methodDictionary.Add(uniqueName, method);
|
|
return method;
|
|
}
|
|
|
|
switch (node.NodeType)
|
|
{
|
|
case NodeType.Vector:
|
|
{
|
|
UniversalNode arrayNode = node.SubNodes[0];
|
|
UniversalNode elementTypeNode = arrayNode.SubNodes[1];
|
|
bool align = node.AlignBytes || arrayNode.AlignBytes;
|
|
if (type is GenericInstanceTypeSignature genericSignature)
|
|
{
|
|
Debug.Assert(genericSignature.GenericType.Name == $"{nameof(AssetList<>)}`1");
|
|
Debug.Assert(genericSignature.TypeArguments.Count == 1);
|
|
method = MakeListMethod(uniqueName, elementTypeNode, genericSignature.TypeArguments[0], version, align);
|
|
}
|
|
else
|
|
{
|
|
SzArrayTypeSignature arrayType = (SzArrayTypeSignature)type;
|
|
TypeSignature elementType = arrayType.BaseType;
|
|
method = MakeArrayMethod(uniqueName, elementTypeNode, elementType, version, align);
|
|
}
|
|
}
|
|
break;
|
|
case NodeType.Map:
|
|
{
|
|
UniversalNode arrayNode = node.SubNodes[0];
|
|
UniversalNode pairNode = arrayNode.SubNodes[1];
|
|
bool align = node.AlignBytes || arrayNode.AlignBytes;
|
|
GenericInstanceTypeSignature genericSignature = (GenericInstanceTypeSignature)type;
|
|
Debug.Assert(genericSignature.GenericType.Name == $"{nameof(AssetDictionary<,>)}`2");
|
|
Debug.Assert(genericSignature.TypeArguments.Count == 2);
|
|
GenericInstanceTypeSignature pairSignature = assetPairReference.MakeGenericInstanceType(genericSignature.TypeArguments[0], genericSignature.TypeArguments[1]);
|
|
method = MakeDictionaryMethod(uniqueName, pairNode, pairSignature, version, align);
|
|
}
|
|
break;
|
|
case NodeType.Pair:
|
|
{
|
|
UniversalNode firstTypeNode = node.SubNodes[0];
|
|
UniversalNode secondTypeNode = node.SubNodes[1];
|
|
bool align = node.AlignBytes;
|
|
GenericInstanceTypeSignature genericSignature = (GenericInstanceTypeSignature)type;
|
|
Debug.Assert(genericSignature.GenericType.Name == $"{nameof(AssetPair<,>)}`2");
|
|
Debug.Assert(genericSignature.TypeArguments.Count == 2);
|
|
method = MakePairMethod(uniqueName, firstTypeNode, genericSignature.TypeArguments[0], secondTypeNode, genericSignature.TypeArguments[1], version, align);
|
|
}
|
|
break;
|
|
case NodeType.TypelessData: //byte array
|
|
{
|
|
method = MakeTypelessDataMethod(uniqueName, node.AlignBytes);
|
|
}
|
|
break;
|
|
case NodeType.Array:
|
|
{
|
|
UniversalNode elementTypeNode = node.SubNodes[1];
|
|
bool align = node.AlignBytes;
|
|
if (type is GenericInstanceTypeSignature genericSignature)
|
|
{
|
|
Debug.Assert(genericSignature.GenericType.Name == $"{nameof(AssetList<>)}`1");
|
|
Debug.Assert(genericSignature.TypeArguments.Count == 1);
|
|
method = MakeListMethod(uniqueName, elementTypeNode, genericSignature.TypeArguments[0], version, align);
|
|
}
|
|
else
|
|
{
|
|
SzArrayTypeSignature arrayType = (SzArrayTypeSignature)type;
|
|
TypeSignature elementType = arrayType.BaseType;
|
|
method = MakeArrayMethod(uniqueName, elementTypeNode, elementType, version, align);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
method = MakePrimitiveMethod(uniqueName, node, node.AlignBytes);
|
|
break;
|
|
}
|
|
|
|
methodDictionary.Add(uniqueName, method);
|
|
return method;
|
|
}
|
|
|
|
private static MethodDefinition MakeGenericAssetAlignMethod()
|
|
{
|
|
string uniqueName = "AssetAlign";
|
|
GenericParameterSignature elementType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 0);
|
|
IMethodDefOrRef readMethod = SharedState.Instance.Importer.ImportMethod<UnityAssetBase>(m => m.Name == ReadMethod && m.Parameters[0].ParameterType is ByReferenceTypeSignature);
|
|
MethodDefinition method = NewMethod(uniqueName, elementType);
|
|
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.Add(CilOpCodes.Callvirt, readMethod);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(alignStreamMethod);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
|
|
GenericParameter genericParameter = new GenericParameter("T");
|
|
genericParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(genericParameter);
|
|
method.Signature!.GenericParameterCount = 1;
|
|
method.Signature.IsGeneric = true;
|
|
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakeDictionaryMethod(string uniqueName, UniversalNode pairNode, GenericInstanceTypeSignature pairSignature, UnityVersion version, bool align)
|
|
{
|
|
TypeSignature keySignature = pairSignature.TypeArguments[0];
|
|
TypeSignature valueSignature = pairSignature.TypeArguments[1];
|
|
if (keySignature.IsTypeDefinition() && valueSignature.IsTypeDefinition())
|
|
{
|
|
return align
|
|
? readAssetDictionaryAlignDefinition.MakeGenericInstanceMethod(keySignature, valueSignature)
|
|
: readAssetDictionaryDefinition.MakeGenericInstanceMethod(keySignature, valueSignature);
|
|
}
|
|
else
|
|
{
|
|
IMethodDescriptor pairReadMethod = GetOrMakeMethod(pairNode, pairSignature, version);
|
|
return MakeDictionaryMethod(uniqueName, pairReadMethod, keySignature, valueSignature, align);
|
|
}
|
|
}
|
|
|
|
private static MethodDefinition MakeGenericDictionaryMethod(bool align)
|
|
{
|
|
string uniqueName = align ? "MapAlign_Asset_Asset" : "Map_Asset_Asset";
|
|
GenericParameterSignature keyType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 0);
|
|
GenericParameterSignature valueType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 1);
|
|
IMethodDescriptor readMethod = readAssetPairDefinition.MakeGenericInstanceMethod(keyType, valueType);
|
|
MethodDefinition method = MakeDictionaryMethod(uniqueName, readMethod, keyType, valueType, align);
|
|
|
|
GenericParameter keyParameter = new GenericParameter("TKey", GenericParameterAttributes.DefaultConstructorConstraint);
|
|
keyParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(keyParameter);
|
|
GenericParameter valueParameter = new GenericParameter("TValue", GenericParameterAttributes.DefaultConstructorConstraint);
|
|
valueParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(valueParameter);
|
|
method.Signature!.GenericParameterCount = 2;
|
|
method.Signature.IsGeneric = true;
|
|
|
|
return method;
|
|
}
|
|
|
|
private static MethodDefinition MakeDictionaryMethod(string uniqueName, IMethodDescriptor pairReadMethod, TypeSignature keySignature, TypeSignature valueSignature, bool align)
|
|
{
|
|
GenericInstanceTypeSignature genericDictionaryType = assetDictionaryReference.MakeGenericInstanceType(keySignature, valueSignature);
|
|
|
|
MethodDefinition clearMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(AssetDictionary<,>), m => m.Name == nameof(AssetDictionary<,>.Clear));
|
|
IMethodDefOrRef clearMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, genericDictionaryType, clearMethodDefinition);
|
|
|
|
MethodDefinition method = NewMethod(uniqueName, genericDictionaryType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
CilLocalVariable countLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
CilLocalVariable iLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
|
|
CilInstructionLabel loopConditionStartList = new();
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Call, clearMethodReference);
|
|
|
|
//Read count
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(readInt32Method);
|
|
instructions.Add(CilOpCodes.Stloc, countLocal);
|
|
|
|
instructions.Add(CilOpCodes.Ldc_I4_0); //Load 0 as an int32
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in count
|
|
|
|
//Create an empty, unconditional branch which will jump down to the loop condition.
|
|
//This converts the do..while loop into a for loop.
|
|
instructions.Add(CilOpCodes.Br, loopConditionStartList);
|
|
|
|
//Now we just read pair, increment i, compare against count, and jump back to here if it's less
|
|
ICilLabel jumpTargetList = instructions.Add(CilOpCodes.Nop).CreateLabel(); //Create a dummy instruction to jump back to
|
|
|
|
MethodDefinition addNewMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(AssetDictionary<,>), m => m.Name == nameof(AssetDictionary<,>.AddNew));
|
|
IMethodDefOrRef addNewMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, genericDictionaryType, addNewMethodDefinition);
|
|
|
|
//Add new and read pair
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(addNewMethodReference);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(pairReadMethod);
|
|
|
|
//Increment i
|
|
instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i local
|
|
instructions.Add(CilOpCodes.Ldc_I4_1); //Load constant 1 as int32
|
|
instructions.Add(CilOpCodes.Add); //Add
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in i local
|
|
|
|
//Jump to start of loop if i < count
|
|
loopConditionStartList.Instruction = instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal); //Load count
|
|
instructions.Add(CilOpCodes.Blt, jumpTargetList); //Jump back up if less than
|
|
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakePairMethod(string uniqueName, UniversalNode keyTypeNode, TypeSignature keySignature, UniversalNode valueTypeNode, TypeSignature valueSignature, UnityVersion version, bool align)
|
|
{
|
|
if (keySignature.IsTypeDefinition() && valueSignature.IsTypeDefinition())
|
|
{
|
|
return align
|
|
? readAssetPairAlignDefinition.MakeGenericInstanceMethod(keySignature, valueSignature)
|
|
: readAssetPairDefinition.MakeGenericInstanceMethod(keySignature, valueSignature);
|
|
}
|
|
else
|
|
{
|
|
IMethodDescriptor keyReadMethod = GetOrMakeMethod(keyTypeNode, keySignature, version);
|
|
IMethodDescriptor valueReadMethod = GetOrMakeMethod(valueTypeNode, valueSignature, version);
|
|
return MakePairMethod(uniqueName, keyReadMethod, keySignature, valueReadMethod, valueSignature, align);
|
|
}
|
|
}
|
|
|
|
private static MethodDefinition MakeGenericPairMethod(bool align)
|
|
{
|
|
string uniqueName = align ? "PairAlign_Asset_Asset" : "Pair_Asset_Asset";
|
|
GenericParameterSignature keyType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 0);
|
|
GenericParameterSignature valueType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 1);
|
|
IMethodDefOrRef readMethod = SharedState.Instance.Importer.ImportMethod<UnityAssetBase>(m => m.Name == ReadMethod && m.Parameters[0].ParameterType is ByReferenceTypeSignature);
|
|
MethodDefinition method = MakePairMethod(uniqueName, readMethod, keyType, readMethod, valueType, align);
|
|
|
|
GenericParameter keyParameter = new GenericParameter("TKey", GenericParameterAttributes.DefaultConstructorConstraint);
|
|
keyParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(keyParameter);
|
|
GenericParameter valueParameter = new GenericParameter("TValue", GenericParameterAttributes.DefaultConstructorConstraint);
|
|
valueParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(valueParameter);
|
|
method.Signature!.GenericParameterCount = 2;
|
|
method.Signature.IsGeneric = true;
|
|
|
|
return method;
|
|
}
|
|
|
|
private static MethodDefinition MakePairMethod(string uniqueName, IMethodDescriptor keyReadMethod, TypeSignature keySignature, IMethodDescriptor valueReadMethod, TypeSignature valueSignature, bool align)
|
|
{
|
|
GenericInstanceTypeSignature genericPairType = assetPairReference.MakeGenericInstanceType(keySignature, valueSignature);
|
|
|
|
MethodDefinition method = NewMethod(uniqueName, genericPairType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
if (keySignature.IsArrayOrPrimitive())
|
|
{
|
|
IMethodDefOrRef setKeyMethod = MethodUtils.MakeMethodOnGenericType(
|
|
SharedState.Instance.Importer,
|
|
genericPairType,
|
|
assetPairDefinition.Methods.Single(m => m.Name == $"set_{nameof(AssetPair<,>.Key)}"));
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);//pair
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(keyReadMethod);
|
|
instructions.AddCall(setKeyMethod);
|
|
}
|
|
else
|
|
{
|
|
IMethodDefOrRef getKeyMethod = MethodUtils.MakeMethodOnGenericType(
|
|
SharedState.Instance.Importer,
|
|
genericPairType,
|
|
assetPairDefinition.Methods.Single(m => m.Name == $"get_{nameof(AssetPair<,>.Key)}"));
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);//pair
|
|
instructions.AddCall(getKeyMethod);
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(keyReadMethod);
|
|
}
|
|
|
|
if (valueSignature.IsArrayOrPrimitive())
|
|
{
|
|
IMethodDefOrRef setValueMethod = MethodUtils.MakeMethodOnGenericType(
|
|
SharedState.Instance.Importer,
|
|
genericPairType,
|
|
assetPairDefinition.Methods.Single(m => m.Name == $"set_{nameof(AssetPair<,>.Value)}"));
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);//pair
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(valueReadMethod);
|
|
instructions.AddCall(setValueMethod);
|
|
}
|
|
else
|
|
{
|
|
IMethodDefOrRef getValueMethod = MethodUtils.MakeMethodOnGenericType(
|
|
SharedState.Instance.Importer,
|
|
genericPairType,
|
|
assetPairDefinition.Methods.Single(m => m.Name == $"get_{nameof(AssetPair<,>.Value)}"));
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);//pair
|
|
instructions.AddCall(getValueMethod);
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(valueReadMethod);
|
|
}
|
|
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ret);
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakeTypelessDataMethod(string uniqueName, bool align)
|
|
{
|
|
CorLibTypeSignature elementType = SharedState.Instance.Importer.UInt8;
|
|
SzArrayTypeSignature arrayType = elementType.MakeSzArrayType();
|
|
MethodDefinition method = NewMethod(uniqueName, arrayType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
CilLocalVariable countLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
CilLocalVariable arrayLocal = instructions.AddLocalVariable(arrayType);
|
|
|
|
//Read count
|
|
instructions.Add(CilOpCodes.Ldarg_0);//reader
|
|
instructions.AddCall(readInt32Method);
|
|
instructions.Add(CilOpCodes.Stloc, countLocal);
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);//reader
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal);
|
|
instructions.AddCall(readBytesMethod);
|
|
instructions.Add(CilOpCodes.Stloc, arrayLocal);
|
|
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ldloc, arrayLocal);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakeListMethod(string uniqueName, UniversalNode elementTypeNode, TypeSignature elementType, UnityVersion version, bool align)
|
|
{
|
|
if (elementType.IsTypeDefinition())
|
|
{
|
|
return align
|
|
? readAssetListAlignDefinition.MakeGenericInstanceMethod(elementType)
|
|
: readAssetListDefinition.MakeGenericInstanceMethod(elementType);
|
|
}
|
|
else
|
|
{
|
|
IMethodDescriptor elementReadMethod = GetOrMakeMethod(elementTypeNode, elementType, version);
|
|
return MakeListMethod(uniqueName, elementType, elementReadMethod, align);
|
|
}
|
|
}
|
|
|
|
private static MethodDefinition MakeGenericListMethod(bool align)
|
|
{
|
|
string uniqueName = align ? "ArrayAlign_Asset" : "Array_Asset";
|
|
GenericParameterSignature elementType = new GenericParameterSignature(SharedState.Instance.Module, GenericParameterType.Method, 0);
|
|
IMethodDefOrRef readMethod = SharedState.Instance.Importer.ImportMethod<UnityAssetBase>(m => m.Name == ReadMethod && m.Parameters[0].ParameterType is ByReferenceTypeSignature);
|
|
MethodDefinition method = MakeListMethod(uniqueName, elementType, readMethod, align);
|
|
|
|
GenericParameter genericParameter = new GenericParameter("T", GenericParameterAttributes.DefaultConstructorConstraint);
|
|
genericParameter.Constraints.Add(new GenericParameterConstraint(SharedState.Instance.Importer.ImportType<UnityAssetBase>()));
|
|
method.GenericParameters.Add(genericParameter);
|
|
method.Signature!.GenericParameterCount = 1;
|
|
method.Signature.IsGeneric = true;
|
|
|
|
return method;
|
|
}
|
|
|
|
private static MethodDefinition MakeListMethod(string uniqueName, TypeSignature elementType, IMethodDescriptor elementReadMethod, bool align)
|
|
{
|
|
GenericInstanceTypeSignature genericListType = assetListReference.MakeGenericInstanceType(elementType);
|
|
|
|
MethodDefinition clearMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(AssetList<>), m => m.Name == nameof(AssetList<>.Clear));
|
|
IMethodDefOrRef clearMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, genericListType, clearMethodDefinition);
|
|
|
|
MethodDefinition method = NewMethod(uniqueName, genericListType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
CilLocalVariable countLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
CilLocalVariable iLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
|
|
CilInstructionLabel loopConditionStartList = new();
|
|
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Call, clearMethodReference);
|
|
|
|
//Read count
|
|
instructions.Add(CilOpCodes.Ldarg_1);//reader
|
|
instructions.AddCall(readInt32Method);
|
|
instructions.Add(CilOpCodes.Stloc, countLocal);
|
|
|
|
instructions.Add(CilOpCodes.Ldc_I4_0); //Load 0 as an int32
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in count
|
|
|
|
//Create an empty, unconditional branch which will jump down to the loop condition.
|
|
//This converts the do..while loop into a for loop.
|
|
instructions.Add(CilOpCodes.Br, loopConditionStartList);
|
|
|
|
//Now we just read pair, increment i, compare against count, and jump back to here if it's less
|
|
ICilLabel jumpTargetList = instructions.Add(CilOpCodes.Nop).CreateLabel(); //Create a dummy instruction to jump back to
|
|
|
|
//Read and add to list
|
|
if (elementType.IsArrayOrPrimitive())
|
|
{
|
|
MethodDefinition addMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(AssetList<>), m => m.Name == nameof(AssetList<>.Add));
|
|
IMethodDefOrRef addMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, genericListType, addMethodDefinition);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(elementReadMethod);
|
|
instructions.AddCall(addMethodReference);
|
|
}
|
|
else
|
|
{
|
|
MethodDefinition addNewMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(AssetList<>), m => m.Name == nameof(AssetList<>.AddNew));
|
|
IMethodDefOrRef addNewMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, genericListType, addNewMethodDefinition);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(addNewMethodReference);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(elementReadMethod);
|
|
}
|
|
|
|
//Increment i
|
|
instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i local
|
|
instructions.Add(CilOpCodes.Ldc_I4_1); //Load constant 1 as int32
|
|
instructions.Add(CilOpCodes.Add); //Add
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in i local
|
|
|
|
//Jump to start of loop if i < count
|
|
loopConditionStartList.Instruction = instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal); //Load count
|
|
instructions.Add(CilOpCodes.Blt, jumpTargetList); //Jump back up if less than
|
|
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakeArrayMethod(string uniqueName, UniversalNode elementTypeNode, TypeSignature elementType, UnityVersion version, bool align)
|
|
{
|
|
if (elementType is CorLibTypeSignature corLibTypeSignature && corLibTypeSignature.ElementType == ElementType.U1)
|
|
{
|
|
return MakeTypelessDataMethod(uniqueName, align);
|
|
}
|
|
|
|
bool throwForNonByteArrays = true;
|
|
if (throwForNonByteArrays)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
IMethodDescriptor elementReadMethod = GetOrMakeMethod(elementTypeNode, elementType, version);
|
|
|
|
SzArrayTypeSignature arrayType = elementType.MakeSzArrayType();
|
|
GenericInstanceTypeSignature listType = SharedState.Instance.Importer.ImportType(typeof(List<>)).MakeGenericInstanceType(elementType);
|
|
MethodDefinition method = NewMethod(uniqueName, arrayType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
|
|
CilLocalVariable countLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
CilLocalVariable iLocal = instructions.AddLocalVariable(SharedState.Instance.Importer.Int32);
|
|
CilLocalVariable arrayLocal = instructions.AddLocalVariable(arrayType);
|
|
CilLocalVariable listLocal = instructions.AddLocalVariable(listType);
|
|
|
|
//Read count
|
|
instructions.Add(CilOpCodes.Ldarg_0);//reader
|
|
instructions.AddCall(readInt32Method);
|
|
instructions.Add(CilOpCodes.Stloc, countLocal);
|
|
|
|
CilInstructionLabel readAsListInstruction = new();
|
|
CilInstructionLabel loopConditionStartArray = new();
|
|
CilInstructionLabel loopConditionStartList = new();
|
|
CilInstructionLabel returnInstruction = new();
|
|
|
|
//Check size of count
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal);
|
|
instructions.Add(CilOpCodes.Ldc_I4, MaxArraySize);
|
|
instructions.Add(CilOpCodes.Bgt, readAsListInstruction);
|
|
|
|
//Read into array
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal);
|
|
instructions.Add(CilOpCodes.Newarr, elementType.ToTypeDefOrRef());
|
|
instructions.Add(CilOpCodes.Stloc, arrayLocal);
|
|
|
|
instructions.Add(CilOpCodes.Ldc_I4_0); //Load 0 as an int32
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in count
|
|
|
|
//Create an empty, unconditional branch which will jump down to the loop condition.
|
|
//This converts the do..while loop into a for loop.
|
|
instructions.Add(CilOpCodes.Br, loopConditionStartArray);
|
|
|
|
//Now we just read pair, increment i, compare against count, and jump back to here if it's less
|
|
ICilLabel jumpTargetArray = instructions.Add(CilOpCodes.Nop).CreateLabel(); //Create a dummy instruction to jump back to
|
|
|
|
//Read and add to array
|
|
instructions.Add(CilOpCodes.Ldloc, arrayLocal);
|
|
instructions.Add(CilOpCodes.Ldloc, iLocal);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(elementReadMethod);
|
|
instructions.AddStoreElement(elementType);
|
|
|
|
//Increment i
|
|
instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i local
|
|
instructions.Add(CilOpCodes.Ldc_I4_1); //Load constant 1 as int32
|
|
instructions.Add(CilOpCodes.Add); //Add
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in i local
|
|
|
|
//Jump to start of loop if i < count
|
|
loopConditionStartArray.Instruction = instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal); //Load count
|
|
instructions.Add(CilOpCodes.Blt, jumpTargetArray); //Jump back up if less than
|
|
|
|
instructions.Add(CilOpCodes.Br, returnInstruction);//Jump to return statement
|
|
|
|
//Read into list (because we don't trust large counts)
|
|
|
|
MethodDefinition listConstructorDefinition = SharedState.Instance.Importer.LookupMethod(typeof(List<>), m =>
|
|
{
|
|
return m.IsConstructor
|
|
&& m.Parameters.Count == 1
|
|
&& m.Parameters[0].ParameterType is CorLibTypeSignature corLibTypeSignature
|
|
&& corLibTypeSignature.ElementType == ElementType.I4;
|
|
});
|
|
IMethodDefOrRef listConstructorReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, listType, listConstructorDefinition);
|
|
MethodDefinition addMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(List<>), m => m.Name == nameof(List<>.Add));
|
|
IMethodDefOrRef addMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, listType, addMethodDefinition);
|
|
MethodDefinition toArrayMethodDefinition = SharedState.Instance.Importer.LookupMethod(typeof(List<>), m => m.Name == nameof(List<>.ToArray));
|
|
IMethodDefOrRef toArrayMethodReference = MethodUtils.MakeMethodOnGenericType(SharedState.Instance.Importer, listType, toArrayMethodDefinition);
|
|
|
|
readAsListInstruction.Instruction = instructions.Add(CilOpCodes.Ldc_I4, MaxArraySize);
|
|
instructions.Add(CilOpCodes.Newobj, listConstructorReference);
|
|
instructions.Add(CilOpCodes.Stloc, listLocal);
|
|
|
|
instructions.Add(CilOpCodes.Ldc_I4_0); //Load 0 as an int32
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in count
|
|
|
|
//Create an empty, unconditional branch which will jump down to the loop condition.
|
|
//This converts the do..while loop into a for loop.
|
|
instructions.Add(CilOpCodes.Br, loopConditionStartList);
|
|
|
|
//Now we just read pair, increment i, compare against count, and jump back to here if it's less
|
|
ICilLabel jumpTargetList = instructions.Add(CilOpCodes.Nop).CreateLabel(); //Create a dummy instruction to jump back to
|
|
|
|
//Read byte and add to list
|
|
instructions.Add(CilOpCodes.Ldloc, listLocal);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(elementReadMethod);
|
|
instructions.AddCall(addMethodReference);
|
|
|
|
//Increment i
|
|
instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i local
|
|
instructions.Add(CilOpCodes.Ldc_I4_1); //Load constant 1 as int32
|
|
instructions.Add(CilOpCodes.Add); //Add
|
|
instructions.Add(CilOpCodes.Stloc, iLocal); //Store in i local
|
|
|
|
//Jump to start of loop if i < count
|
|
loopConditionStartList.Instruction = instructions.Add(CilOpCodes.Ldloc, iLocal); //Load i
|
|
instructions.Add(CilOpCodes.Ldloc, countLocal); //Load count
|
|
instructions.Add(CilOpCodes.Blt, jumpTargetList); //Jump back up if less than
|
|
|
|
instructions.Add(CilOpCodes.Ldloc, listLocal);
|
|
instructions.AddCall(toArrayMethodReference);
|
|
instructions.Add(CilOpCodes.Stloc, arrayLocal);
|
|
|
|
returnInstruction.Instruction = instructions.Add(CilOpCodes.Nop);
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ldloc, arrayLocal);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
return method;
|
|
}
|
|
|
|
private static IMethodDescriptor MakePrimitiveMethod(string uniqueName, UniversalNode node, bool align)
|
|
{
|
|
IMethodDescriptor primitiveMethod = GetPrimitiveMethod(node);
|
|
if (align)
|
|
{
|
|
MethodDefinition method = NewMethod(uniqueName, primitiveMethod.Signature!.ReturnType);
|
|
CilInstructionCollection instructions = method.GetInstructions();
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(primitiveMethod);
|
|
if (align)
|
|
{
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.AddCall(alignStreamMethod);
|
|
}
|
|
instructions.Add(CilOpCodes.Ret);
|
|
return method;
|
|
}
|
|
else
|
|
{
|
|
return primitiveMethod;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Array and primitive read methods have the Func<AssetReader, T> signature.<br/>
|
|
/// Others have the Action<T, AssetReader> signature.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
private static bool IsArrayOrPrimitive(this TypeSignature type)
|
|
{
|
|
return type is SzArrayTypeSignature or CorLibTypeSignature or TypeDefOrRefSignature { Namespace: "AssetRipper.Primitives", Name: nameof(Utf8String) };
|
|
}
|
|
|
|
private static bool IsTypeDefinition(this TypeSignature type)
|
|
{
|
|
return type is TypeDefOrRefSignature defOrRefSignature && defOrRefSignature.Type is TypeDefinition;
|
|
}
|
|
|
|
private static IMethodDescriptor GetPrimitiveMethod(UniversalNode node)
|
|
{
|
|
return node.NodeType switch
|
|
{
|
|
NodeType.Boolean => primitiveReadMethods[ElementType.Boolean],
|
|
NodeType.Character => primitiveReadMethods[ElementType.Char],
|
|
NodeType.Int8 => primitiveReadMethods[ElementType.I1],
|
|
NodeType.UInt8 => primitiveReadMethods[ElementType.U1],
|
|
NodeType.Int16 => primitiveReadMethods[ElementType.I2],
|
|
NodeType.UInt16 => primitiveReadMethods[ElementType.U2],
|
|
NodeType.Int32 => primitiveReadMethods[ElementType.I4],
|
|
NodeType.UInt32 => primitiveReadMethods[ElementType.U4],
|
|
NodeType.Int64 => primitiveReadMethods[ElementType.I8],
|
|
NodeType.UInt64 => primitiveReadMethods[ElementType.U8],
|
|
NodeType.Single => primitiveReadMethods[ElementType.R4],
|
|
NodeType.Double => primitiveReadMethods[ElementType.R8],
|
|
NodeType.String => primitiveReadMethods[ElementType.String],
|
|
_ => throw new NotSupportedException(node.TypeName),
|
|
};
|
|
}
|
|
|
|
private static MethodDefinition NewMethod(string uniqueName, TypeSignature parameter)
|
|
{
|
|
if (parameter.IsArrayOrPrimitive())
|
|
{
|
|
MethodSignature methodSignature = MethodSignature.CreateStatic(parameter);
|
|
MethodDefinition method = new MethodDefinition($"{ReadMethod}_{uniqueName}", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, methodSignature);
|
|
method.CilMethodBody = new CilMethodBody();
|
|
method.AddParameter(endianSpanReaderReference, "reader");
|
|
method.AddExtensionAttribute(SharedState.Instance.Importer);
|
|
return method;
|
|
}
|
|
else
|
|
{
|
|
MethodSignature methodSignature = MethodSignature.CreateStatic(SharedState.Instance.Importer.Void);
|
|
MethodDefinition method = new MethodDefinition($"{ReadMethod}_{uniqueName}", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, methodSignature);
|
|
method.CilMethodBody = new CilMethodBody();
|
|
method.AddParameter(parameter, "value");
|
|
method.AddParameter(endianSpanReaderReference, "reader");
|
|
method.AddExtensionAttribute(SharedState.Instance.Importer);
|
|
return method;
|
|
}
|
|
}
|
|
|
|
private static CilInstruction AddCall(this CilInstructionCollection instructions, IMethodDescriptor method)
|
|
{
|
|
return method is MethodDefinition definition && definition.IsStatic
|
|
? instructions.Add(CilOpCodes.Call, method)
|
|
: instructions.Add(CilOpCodes.Callvirt, method);
|
|
}
|
|
|
|
private static IMethodDefOrRef GetDefaultConstructor(this TypeSignature type)
|
|
{
|
|
return type switch
|
|
{
|
|
TypeDefOrRefSignature typeDefOrRefSignature => typeDefOrRefSignature.Type is TypeDefinition typeDefinition
|
|
? typeDefinition.GetDefaultConstructor()
|
|
: throw new InvalidOperationException(),
|
|
GenericInstanceTypeSignature genericInstanceTypeSignature => MethodUtils.MakeConstructorOnGenericType(SharedState.Instance.Importer, genericInstanceTypeSignature, 0),
|
|
_ => throw new NotSupportedException(),
|
|
};
|
|
}
|
|
}
|