merge field rewriters to reduce rewrite iterations
This commit is contained in:
parent
1ea8d75235
commit
230f119205
|
@ -8,6 +8,7 @@
|
|||
* Added automatic save recovery when the custom farm type isn't available anymore.
|
||||
* Added the new game build number to the SMAPI console + log.
|
||||
* The installer now detects Xbox app game folders.
|
||||
* Reduced mod loading time a bit.
|
||||
* Fixed extra newlines shown in the console in non-developer mode.
|
||||
* Fixed macOS launch issue when using some terminals (thanks to bruce2409!).
|
||||
* Fixed Linux/macOS terminal ignoring backspace in Stardew Valley 1.5.5+.
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
|
|||
** Fields
|
||||
*********/
|
||||
/// <summary>The comparer which heuristically compares type definitions.</summary>
|
||||
private static readonly TypeReferenceComparer TypeDefinitionComparer = new TypeReferenceComparer();
|
||||
private static readonly TypeReferenceComparer TypeDefinitionComparer = new();
|
||||
|
||||
|
||||
/*********
|
||||
|
@ -28,28 +28,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
|
|||
: null;
|
||||
}
|
||||
|
||||
/// <summary>Get whether the field is a reference to the expected type and field.</summary>
|
||||
/// <param name="instruction">The IL instruction.</param>
|
||||
/// <param name="fullTypeName">The full type name containing the expected field.</param>
|
||||
/// <param name="fieldName">The name of the expected field.</param>
|
||||
public static bool IsFieldReferenceTo(Instruction instruction, string fullTypeName, string fieldName)
|
||||
{
|
||||
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
|
||||
return RewriteHelper.IsFieldReferenceTo(fieldRef, fullTypeName, fieldName);
|
||||
}
|
||||
|
||||
/// <summary>Get whether the field is a reference to the expected type and field.</summary>
|
||||
/// <param name="fieldRef">The field reference to check.</param>
|
||||
/// <param name="fullTypeName">The full type name containing the expected field.</param>
|
||||
/// <param name="fieldName">The name of the expected field.</param>
|
||||
public static bool IsFieldReferenceTo(FieldReference fieldRef, string fullTypeName, string fieldName)
|
||||
{
|
||||
return
|
||||
fieldRef != null
|
||||
&& fieldRef.DeclaringType.FullName == fullTypeName
|
||||
&& fieldRef.Name == fieldName;
|
||||
}
|
||||
|
||||
/// <summary>Get the method reference from an instruction if it matches.</summary>
|
||||
/// <param name="instruction">The IL instruction.</param>
|
||||
public static MethodReference AsMethodReference(Instruction instruction)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
@ -12,54 +13,55 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
|||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The type containing the field to which references should be rewritten.</summary>
|
||||
private readonly Type Type;
|
||||
|
||||
/// <summary>The field name to which references should be rewritten.</summary>
|
||||
private readonly string FromFieldName;
|
||||
|
||||
/// <summary>The new field to reference.</summary>
|
||||
private readonly FieldInfo ToField;
|
||||
/// <summary>The new fields to reference indexed by the old field/type names.</summary>
|
||||
private readonly Dictionary<string, Dictionary<string, FieldInfo>> FieldMaps = new();
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
public FieldReplaceRewriter()
|
||||
: base(defaultPhrase: "field replacement") { } // will be overridden when a field is replaced
|
||||
|
||||
/// <summary>Add a field to replace.</summary>
|
||||
/// <param name="fromType">The type whose field to rewrite.</param>
|
||||
/// <param name="fromFieldName">The field name to rewrite.</param>
|
||||
/// <param name="toType">The new type which will have the field.</param>
|
||||
/// <param name="toFieldName">The new field name to reference.</param>
|
||||
public FieldReplaceRewriter(Type fromType, string fromFieldName, Type toType, string toFieldName)
|
||||
: base(defaultPhrase: $"{fromType.FullName}.{fromFieldName} field")
|
||||
public FieldReplaceRewriter AddField(Type fromType, string fromFieldName, Type toType, string toFieldName)
|
||||
{
|
||||
this.Type = fromType;
|
||||
this.FromFieldName = fromFieldName;
|
||||
this.ToField = toType.GetField(toFieldName);
|
||||
if (this.ToField == null)
|
||||
throw new InvalidOperationException($"The {toType.FullName} class doesn't have a {toFieldName} field.");
|
||||
}
|
||||
// get full type name
|
||||
string fromTypeName = fromType?.FullName;
|
||||
if (fromTypeName == null)
|
||||
throw new InvalidOperationException($"Can't replace field for invalid type reference {toType}.");
|
||||
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="type">The type whose field to rewrite.</param>
|
||||
/// <param name="fromFieldName">The field name to rewrite.</param>
|
||||
/// <param name="toFieldName">The new field name to reference.</param>
|
||||
public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName)
|
||||
: this(type, fromFieldName, type, toFieldName)
|
||||
{
|
||||
// get target field
|
||||
FieldInfo toField = toType.GetField(toFieldName);
|
||||
if (toField == null)
|
||||
throw new InvalidOperationException($"The {toType.FullName} class doesn't have a {toFieldName} field.");
|
||||
|
||||
// add mapping
|
||||
if (!this.FieldMaps.TryGetValue(fromTypeName, out var fieldMap))
|
||||
this.FieldMaps[fromTypeName] = fieldMap = new();
|
||||
fieldMap[fromFieldName] = toField;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
|
||||
{
|
||||
// get field reference
|
||||
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
|
||||
if (!RewriteHelper.IsFieldReferenceTo(fieldRef, this.Type.FullName, this.FromFieldName))
|
||||
string declaringType = fieldRef?.DeclaringType?.FullName;
|
||||
|
||||
// get mapped field
|
||||
if (declaringType == null || !this.FieldMaps.TryGetValue(declaringType, out var fieldMap) || !fieldMap.TryGetValue(fieldRef.Name, out FieldInfo toField))
|
||||
return false;
|
||||
|
||||
// replace with new field
|
||||
instruction.Operand = module.ImportReference(this.ToField);
|
||||
|
||||
this.Phrases.Add($"{fieldRef.DeclaringType.Name}.{fieldRef.Name} field");
|
||||
instruction.Operand = module.ImportReference(toField);
|
||||
return this.MarkRewritten();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,10 @@ namespace StardewModdingAPI.Metadata
|
|||
if (rewriteMods)
|
||||
{
|
||||
// rewrite for Stardew Valley 1.5
|
||||
yield return new FieldReplaceRewriter(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture));
|
||||
yield return new FieldReplaceRewriter(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
||||
yield return new FieldReplaceRewriter(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
||||
yield return new FieldReplaceRewriter()
|
||||
.AddField(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture))
|
||||
.AddField(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps))
|
||||
.AddField(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
||||
|
||||
// heuristic rewrites
|
||||
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
|
||||
|
|
Loading…
Reference in New Issue