Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 6 additions & 22 deletions KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;

namespace KSPCommunityFixes.BugFixes
{
Expand All @@ -11,31 +9,17 @@ class DoubleCurvePreserveTangents : BasePatch

protected override void ApplyPatches()
{
AddPatch(PatchType.Transpiler, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents));
AddPatch(PatchType.Prefix, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents));
}

static IEnumerable<CodeInstruction> DoubleCurve_RecomputeTangents_Transpiler(IEnumerable<CodeInstruction> instructions)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool DoubleCurve_RecomputeTangents_Prefix(DoubleCurve __instance)
{
// The existing function has a test if ( count == 1 ) and, if true, it
// will flatten the tangents of the key regardless of if it is
// set to autotangent or not. Since the tangents of a single-key
// curve don't matter, let's just return.
List<CodeInstruction> code = new List<CodeInstruction>(instructions);
for (int i = 1; i < code.Count; ++i)
{
if (code[i].opcode == OpCodes.Ldc_I4_1 && code[i - 1].opcode != OpCodes.Ldloc_1)
{
code[i] = new CodeInstruction(OpCodes.Ret);
code[i + 1] = new CodeInstruction(OpCodes.Nop);
code[i + 2] = new CodeInstruction(OpCodes.Nop);
code[i + 3] = new CodeInstruction(OpCodes.Nop);
code[i + 4] = new CodeInstruction(OpCodes.Nop);
code[i + 5] = new CodeInstruction(OpCodes.Nop);
break;
}
}

return code;
// curve don't matter, we skip the function in that case.
return __instance.keys.Count != 1;
}
}
}
107 changes: 94 additions & 13 deletions KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HarmonyLib;
using CommNet.Network;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Reflection;
Expand All @@ -22,7 +23,6 @@ class ExtendedDeployableParts : BasePatch
protected override void ApplyPatches()
{
AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM));

AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart));
}

Expand Down Expand Up @@ -85,25 +85,106 @@ static IEnumerable<CodeInstruction> ModuleDeployableSolarPanel_OnStart_Transpile
// anim.Stop(animationName);
//}

// We remove that entire if statement by replacing the if (Brtrue_S) by an unconditional jump (Br_S)
// We remove that entire if statement by making the if (Brtrue_S) unconditional

FieldInfo ModuleDeployablePart_deployState = AccessTools.Field(typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.deployState));

List<CodeInstruction> code = new List<CodeInstruction>(instructions);
var matcher = new CodeMatcher(instructions);
matcher
.MatchStartForward(
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, ModuleDeployablePart_deployState),
new CodeMatch(OpCodes.Brtrue_S)
);

for (int i = 0; i < code.Count; i++)
if (!matcher.IsValid)
return matcher.Instructions();

matcher
.RemoveInstructions(2)
.Insert(new CodeInstruction(OpCodes.Ldc_I4_1));

return matcher.Instructions();
}

static bool ModuleDeployablePart_startFSM_Prefix(ModuleDeployableSolarPanel __instance)
{
if (__instance.useAnimation)
{
if (code[i].opcode == OpCodes.Ldarg_0
&& code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i+1].operand, ModuleDeployablePart_deployState)
&& code[i + 2].opcode == OpCodes.Brtrue_S)
__instance.anim[__instance.animationName].wrapMode = WrapMode.ClampForever;
switch (__instance.deployState)
{
code[i].opcode = OpCodes.Br_S;
code[i].operand = code[i + 2].operand; // grab the target instruction from the original jump
break;
case ModuleDeployablePart.DeployState.RETRACTED:
__instance.anim[__instance.animationName].normalizedTime = 0f;
__instance.anim[__instance.animationName].enabled = true;
__instance.anim[__instance.animationName].weight = 1f;
__instance.anim[__instance.animationName].speed = 0f;
__instance.bypassSetupAnimation = true;
__instance.Events["Retract"].active = false;
__instance.Events["Extend"].active = true;
break;
case ModuleDeployablePart.DeployState.EXTENDED:
__instance.anim[__instance.animationName].normalizedTime = 1f;
__instance.anim[__instance.animationName].enabled = true;
__instance.anim[__instance.animationName].speed = 0f;
__instance.anim[__instance.animationName].weight = 1f;
__instance.Events["Extend"].active = false;
__instance.Events["Retract"].active = __instance.retractable || HighLogic.LoadedSceneIsEditor;
if (__instance.hasPivot)
{
__instance.panelRotationTransform.localRotation = __instance.currentRotation;
}
break;
case ModuleDeployablePart.DeployState.RETRACTING:
__instance.Events["Retract"].active = false;
__instance.Events["Extend"].active = false;
break;
case ModuleDeployablePart.DeployState.EXTENDING:
__instance.Events["Retract"].active = false;
__instance.Events["Extend"].active = false;
break;
}
if (__instance.deployState == ModuleDeployablePart.DeployState.RETRACTING || __instance.deployState == ModuleDeployablePart.DeployState.EXTENDING || __instance.deployState == ModuleDeployablePart.DeployState.BROKEN)
{
__instance.anim[__instance.animationName].normalizedTime = __instance.storedAnimationTime;
__instance.anim[__instance.animationName].speed = __instance.storedAnimationSpeed;
}
if (!__instance.bypassSetupAnimation)
{
__instance.anim.Play(__instance.animationName);
}
if (!__instance.playAnimationOnStart && __instance.deployState != ModuleDeployablePart.DeployState.EXTENDING && __instance.deployState != ModuleDeployablePart.DeployState.RETRACTING)
{
__instance.stopAnimation = true;
}
}

return code;
else
{
if (__instance.hasPivot)
{
__instance.panelRotationTransform.localRotation = __instance.originalRotation;
}
if (__instance.deployState != ModuleDeployablePart.DeployState.BROKEN)
{
__instance.deployState = ModuleDeployablePart.DeployState.EXTENDED;
}
__instance.Events["Retract"].active = false;
__instance.Events["Extend"].active = false;
__instance.Actions["ExtendPanelsAction"].active = false;
__instance.Actions["ExtendAction"].active = false;
__instance.Actions["RetractAction"].active = false;
__instance.Fields["status"].guiActiveEditor = false;
}
if (__instance.deployState == ModuleDeployablePart.DeployState.BROKEN)
{
__instance.Events["Retract"].active = false;
__instance.Events["Extend"].active = false;
if (__instance.panelBreakTransform)
{
__instance.panelBreakTransform.gameObject.SetActive(false);
}
}
return false;
}
}
}
133 changes: 63 additions & 70 deletions KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public class ModuleIndexingMismatch : BasePatch
private const string VALUENAME_MODULEPARTCONFIGID = "modulePartConfigId";
private static readonly HashSet<string> multiModules = new HashSet<string>();
private static readonly Dictionary<string, Type> allModuleTypes = new Dictionary<string, Type>();

protected override Version VersionMin => new Version(1, 8, 0);

protected override void ApplyPatches()
Expand All @@ -44,7 +43,6 @@ protected override void ApplyPatches()
{
AddPatch(PatchType.Transpiler, typeof(ProtoPartSnapshot), "ConfigurePart");
}

AddPatch(PatchType.Transpiler, typeof(ShipConstruct), "LoadShip", new Type[] { typeof(ConfigNode), typeof(uint), typeof(bool), typeof(string).MakeByRefType() });

Type multiModuleType = typeof(IMultipleModuleInPart);
Expand Down Expand Up @@ -222,6 +220,7 @@ private static void LoadProtoPartSnapshotModule(ProtoPartModuleSnapshot protoMod
protoModule.moduleRef = module;
}


static void LoadModules(ProtoPartSnapshot protoPart, Part part)
Comment thread
R-T-B marked this conversation as resolved.
{
int protoModuleCount = protoPart.modules.Count;
Expand Down Expand Up @@ -348,7 +347,7 @@ static void LoadModules(ProtoPartSnapshot protoPart, Part part)
{
// see https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/236
// when persisted type is a derived type of the module type, or when the module type is a derived type of the persisted type, still attempt to load it.
if (protoModuleIdx < partModuleCount && allModuleTypes.TryGetValue(protoModule.moduleName, out Type moduleType)
if (protoModuleIdx < partModuleCount && allModuleTypes.TryGetValue(protoModule.moduleName, out Type moduleType)
&& (part.Modules[protoModuleIdx].GetType().IsAssignableFrom(moduleType) || moduleType.IsInstanceOfType(part.Modules[protoModuleIdx])))
{
LoadProtoPartSnapshotModule(protoPart.modules[protoModuleIdx], part.modules[protoModuleIdx]);
Expand All @@ -375,75 +374,69 @@ static IEnumerable<CodeInstruction> ShipConstruct_LoadShip_Transpiler(IEnumerabl
MethodInfo LoadShipModuleNodes = AccessTools.Method(typeof(ModuleIndexingMismatch), nameof(ModuleIndexingMismatch.LoadShipModuleNodes));
MethodInfo ConfigNode_get_nodes = AccessTools.PropertyGetter(typeof(ConfigNode), nameof(ConfigNode.nodes));

List<CodeInstruction> code = new List<CodeInstruction>(instructions);
var matcher = new CodeMatcher(instructions);

// first, remove the original module load call
bool originalFound = false;
for (int i = 0; i < code.Count - 6; i++)
{
//// part.LoadModule(configNode2, ref moduleIndex);
// ldloc.s 6
// ldloc.3
// ldloca.s 39
// callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&)
// dup
// pop
// pop
if (code[i].opcode == OpCodes.Ldloc_S
&& code[i + 1].opcode == OpCodes.Ldloc_3
&& code[i + 2].opcode == OpCodes.Ldloca_S
&& code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule)
&& code[i + 4].opcode == OpCodes.Dup
&& code[i + 5].opcode == OpCodes.Pop
&& code[i + 6].opcode == OpCodes.Pop)
{
originalFound = true;
for (int j = i; j < i + 7; j++)
{
code[j].opcode = OpCodes.Nop;
code[j].operand = null;
}
break;
}
}

if (!originalFound)
{
Debug.LogError($"Error applying ModuleIndexingMismatch patch : couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()");
return instructions;
}
//// part.LoadModule(configNode2, ref moduleIndex);
// ldloc.s 6
// ldloc.3
// ldloca.s 39
// callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&)
// dup ; only if obfuscated
// pop ; only if obfuscated
// pop
matcher
.MatchStartForward(
new CodeMatch(OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldloc_3),
new CodeMatch(OpCodes.Ldloca_S),
new CodeMatch(OpCodes.Callvirt, Part_LoadModule)
)
.ThrowIfInvalid("couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()");

// The dup;pop; pair might not be present in a deobfuscated assembly
// so we push a dummy value on the stack instead of deleting them.
//
// One of these appears to have a label, so removing it causes patching
// to fail. The same applies to using SetInstruction.
matcher
.SetAndAdvance(OpCodes.Nop, null)
.SetAndAdvance(OpCodes.Nop, null)
.SetAndAdvance(OpCodes.Nop, null)
.SetAndAdvance(OpCodes.Ldnull, null);

// then, insert our own module loading call before the original part nodes parsing loop
for (int i = 0; i < code.Count - 6; i++)
{
//// int moduleIndex = 0;
// ldc.i4.0 NULL
// stloc.s 39(System.Int32)
//// int m = 0;
// ldc.i4.0 NULL
// stloc.s 45(System.Int32)
//// int count5 = configNode.nodes.Count
// ldloc.2 NULL
// callvirt ConfigNodeList ConfigNode::get_nodes()
// dup NULL
// pop NULL
// callvirt System.Int32 ConfigNodeList::get_Count()

if (code[i].opcode == OpCodes.Ldc_I4_0
&& code[i + 1].opcode == OpCodes.Stloc_S
&& code[i + 2].opcode == OpCodes.Ldc_I4_0
&& code[i + 3].opcode == OpCodes.Stloc_S
&& code[i + 4].opcode == OpCodes.Ldloc_2
&& code[i + 5].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 5].operand, ConfigNode_get_nodes))
{
code.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, 6)); // ldloc.s 6 is the Part local variable
code.Insert(i + 1, new CodeInstruction(OpCodes.Ldloc_2)); // Ldloc_2 is the part ConfigNode variable
code.Insert(i + 2, new CodeInstruction(OpCodes.Call, LoadShipModuleNodes));
break;
}
}

return code;
//// int moduleIndex = 0;
// ldc.i4.0 NULL
// stloc.s 39(System.Int32)
//// int m = 0;
// ldc.i4.0 NULL
// stloc.s 45(System.Int32)
//// int count5 = configNode.nodes.Count
// ldloc.2 NULL
// callvirt ConfigNodeList ConfigNode::get_nodes()
// dup NULL ; only if obfuscated
// pop NULL ; only obfuscated
// callvirt System.Int32 ConfigNodeList::get_Count()
matcher
.Start()
.MatchStartForward(
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Stloc_S),
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Stloc_S),
new CodeMatch(OpCodes.Ldloc_2),
new CodeMatch(OpCodes.Callvirt, ConfigNode_get_nodes)
)
.ThrowIfInvalid("Could not find the call to ConfigNode::get_nodes")
.Advance(6)
.Insert(
new CodeInstruction(OpCodes.Ldloc_S, 6), // Part local variable
new CodeInstruction(OpCodes.Ldloc_2), // part ConfigNode variable
new CodeInstruction(OpCodes.Call, LoadShipModuleNodes)
);

return matcher.Instructions();
}

private static readonly List<ConfigNode> currentModuleNodes = new List<ConfigNode>();
Expand Down Expand Up @@ -589,7 +582,7 @@ static void LoadShipModuleNodes(Part part, ConfigNode partNode)
{
// see https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/236
// when persisted type is a derived type of the module type, or when the module type is a derived type of the persisted type, still attempt to load it.
if (moduleNodeIdx < partModuleCount
if (moduleNodeIdx < partModuleCount
&& allModuleTypes.TryGetValue(nodeModuleName, out Type moduleType)
&& (part.Modules[moduleNodeIdx].GetType().IsAssignableFrom(moduleType) || moduleType.IsInstanceOfType(part.Modules[moduleNodeIdx])))
{
Expand All @@ -611,4 +604,4 @@ static void LoadShipModuleNodes(Part part, ConfigNode partNode)
}
}
}
}
}
Loading