Files
SPT-Server-Build/Patches/Ceciler.JsonExtensionData/JsonExtensionDataPatch.cs
T
2025-10-20 09:09:04 +01:00

111 lines
4.5 KiB
C#

using Ceciler.Interfaces;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
namespace Ceciler.JsonExtensionData;
public class JsonExtensionDataPatch : IPatcher
{
private TypeReference? _dictionaryStringObjectReference;
public void Patch(AssemblyDefinition assembly)
{
var sptReferenceType = assembly.MainModule.Types.First(t => t.FullName == "SPTarkov.Server.Core.Utils.Reference.StaticReferences");
var propertyReferenceType = sptReferenceType.Properties.First(p => p.Name == "Reference");
var fieldReferenceType = sptReferenceType.Fields.First(p => p.Name == "_reference");
_dictionaryStringObjectReference = propertyReferenceType.PropertyType;
// We need to steal from the constructor the IL line 2 (index 1)
var createDictionaryReference = sptReferenceType.GetConstructors().First(c => c.Parameters.Count == 0).Body.Instructions[1];
var processed = new HashSet<string>();
foreach (var typeDefinition in assembly.MainModule.Types)
{
if (
!typeDefinition.Namespace.Contains("SPTarkov.Server.Core.Models")
|| typeDefinition.IsInterface
|| typeDefinition.IsEnum
|| IsStaticClass(typeDefinition)
|| processed.Contains(typeDefinition.FullName)
|| typeDefinition.IsAbstract
|| typeDefinition.HasGenericParameters
)
{
continue;
}
var propertyDefinition = new PropertyDefinition("ExtensionData", PropertyAttributes.None, _dictionaryStringObjectReference);
propertyDefinition.CustomAttributes.Add(propertyReferenceType.CustomAttributes.First());
// Add backing field
var field = new FieldDefinition(
"_extensionData",
FieldAttributes.Private | FieldAttributes.InitOnly,
_dictionaryStringObjectReference
);
field.CustomAttributes.Add(fieldReferenceType.CustomAttributes.First());
typeDefinition.Fields.Add(field);
// Add getter
var get = new MethodDefinition(
"get_ExtensionData",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
_dictionaryStringObjectReference
);
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, field));
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
// Add setter
var set = new MethodDefinition(
"set_ExtensionData",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
assembly.MainModule.TypeSystem.Void
);
set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, _dictionaryStringObjectReference));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
propertyDefinition.SetMethod = set;
propertyDefinition.GetMethod = get;
typeDefinition.Methods.Add(set);
typeDefinition.Methods.Add(get);
typeDefinition.Properties.Add(propertyDefinition);
foreach (var methodDefinition in typeDefinition.GetConstructors().Where(c => !c.IsStatic))
{
var ilCtor = methodDefinition.Body.GetILProcessor();
var loadArg = ilCtor.Create(OpCodes.Ldarg_0);
var createObj = createDictionaryReference;
var setField = ilCtor.Create(OpCodes.Stfld, field);
var first = ilCtor.Body.Instructions.First();
ilCtor.InsertBefore(first, loadArg);
ilCtor.InsertAfter(loadArg, createObj);
ilCtor.InsertAfter(createObj, setField);
}
processed.Add(typeDefinition.FullName);
}
var writerParams = new WriterParameters { WriteSymbols = true };
assembly.Write(writerParams);
}
private bool IsStaticClass(TypeDefinition type)
{
return type.IsClass && type.IsAbstract && type.IsSealed;
}
public string Name
{
get { return "ExtensionData"; }
}
}