summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md1
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs20
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs24
3 files changed, 44 insertions, 1 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index 05f17a2f..6b8cfc3d 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -11,6 +11,7 @@
* For players:
* Fixed errors on Linux/Mac due to mods with incorrect filename case.
* Fixed map rendering crash due to conflict between SMAPI and PyTK.
+ * Fixed error in heuristically-rewritten mods in rare cases (thanks to ZaneYork!).
* For modders:
* All content pack file paths accessed through `IContentPack` are now case-insensitive.
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
index ea29550a..10f68f0d 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
@@ -111,21 +111,39 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
foreach (VariableDefinition variable in method.Body.Variables)
changed |= this.RewriteTypeReference(variable.VariableType, newType => variable.VariableType = newType);
- // check CIL instructions
+ // rewrite CIL instructions
ILProcessor cil = method.Body.GetILProcessor();
Collection<Instruction> instructions = cil.Body.Instructions;
+ bool addedInstructions = false;
for (int i = 0; i < instructions.Count; i++)
{
var instruction = instructions[i];
if (instruction.OpCode.Code == Code.Nop)
continue;
+ int oldCount = cil.Body.Instructions.Count;
changed |= this.RewriteInstruction(instruction, cil, newInstruction =>
{
changed = true;
cil.Replace(instruction, newInstruction);
instruction = newInstruction;
});
+
+ if (cil.Body.Instructions.Count > oldCount)
+ addedInstructions = true;
+ }
+
+ // special case: added instructions may cause an instruction to be out of range
+ // of a short jump that references it
+ if (addedInstructions)
+ {
+ foreach (var instruction in instructions)
+ {
+ var longJumpCode = RewriteHelper.GetEquivalentLongJumpCode(instruction.OpCode);
+ if (longJumpCode != null)
+ instruction.OpCode = longJumpCode.Value;
+ }
+ changed = true;
}
}
}
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
index 207b6445..1f7834ce 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
@@ -77,6 +77,30 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
};
}
+ /// <summary>Get the long equivalent for a short-jump op code.</summary>
+ /// <param name="shortJumpCode">The short-jump op code.</param>
+ /// <returns>Returns the instruction, or <c>null</c> if it isn't a short jump.</returns>
+ public static OpCode? GetEquivalentLongJumpCode(OpCode shortJumpCode)
+ {
+ return shortJumpCode.Code switch
+ {
+ Code.Beq_S => OpCodes.Beq,
+ Code.Bge_S => OpCodes.Bge,
+ Code.Bge_Un_S => OpCodes.Bge_Un,
+ Code.Bgt_S => OpCodes.Bgt,
+ Code.Bgt_Un_S => OpCodes.Bgt_Un,
+ Code.Ble_S => OpCodes.Ble,
+ Code.Ble_Un_S => OpCodes.Ble_Un,
+ Code.Blt_S => OpCodes.Blt,
+ Code.Blt_Un_S => OpCodes.Blt_Un,
+ Code.Bne_Un_S => OpCodes.Bne_Un,
+ Code.Br_S => OpCodes.Br,
+ Code.Brfalse_S => OpCodes.Brfalse,
+ Code.Brtrue_S => OpCodes.Brtrue,
+ _ => null
+ };
+ }
+
/// <summary>Get whether a type matches a type reference.</summary>
/// <param name="type">The defined type.</param>
/// <param name="reference">The type reference.</param>