/// <summary>Saves the patch data to an XmlNode</summary> /// <param name="output">The node where the Instructions will be added as childnodes</param> /// <returns>Returns true if it succeeded, false otherwise</returns> public override bool Save(XmlNode output) { if (methodDefinition == null) { Log.Write(Log.Level.Careful, $"The PatchAction(ILMethodFixed) {Name} is empty and won't be saved!"); return(false); } NameCompressor nc = NameCompressor.Instance; output.Attributes[nc[SST.PatchType]].Value = PatchActionType.ToString(); output.Attributes[nc[SST.Name]].Value = this.Name; instructPatchList = instructPatchList.FindAll(x => !x.Delete || x.IsOld); XmlNode xListPatched = output.InsertCompressedElement(SST.PatchList); xListPatched.CreateAttribute(SST.MethodPath, DataStruct.ReferenceTable.Reference(methodDefinition).ToBaseAlph()); xListPatched.CreateAttribute(SST.InstructionCount, OriginalInstructionCount.ToString()); var xmlInstructionOriginalMethod = new XMLInstruction <Instruction>( DataStruct.ReferenceTable, methodDefinition.Body.Instructions, (list, instruct) => list.IndexOf(instruct)); var xmlInstructionModifiedList = new XMLInstruction <InstructionInfo>( DataStruct.ReferenceTable, instructPatchList, (list, instruct) => { var res = list.FirstOrDefault(x => x.NewInstruction == instruct); return(res != null ? res.NewInstructionNum : -1); }); int instructionPos = 0; foreach (InstructionInfo II in instructPatchList) { XmlNode xInstruction = xListPatched.InsertCompressedElement(SST.Instruction); II.NewInstructionNum = instructionPos; if (II.IsOld) { //OldInstructionNum/OriginalInstructionNum: -1 if new command xInstruction.CreateAttribute(SST.InstructionNum, II.OldInstructionNum.ToString()); xInstruction.CreateAttribute(SST.OpCode, II.OldInstruction.OpCode.Name); xInstruction.CreateAttribute(SST.Delete, nc[II.Delete ? SST.True : SST.False]); xmlInstructionOriginalMethod.Operand2Node(xInstruction, II.OldInstruction); if (!II.Delete) { instructionPos++; XmlNode patchelem = null; if (II.InstructionNumPatch) { if (patchelem == null) { patchelem = xInstruction.CreateCompressedElement(SST.InstructionPatch); } patchelem.CreateAttribute(SST.InstructionNum, II.NewInstructionNum.ToString()); } if (II.InstructionOpCodePatch) { if (patchelem == null) { patchelem = xInstruction.CreateCompressedElement(SST.InstructionPatch); } patchelem.CreateAttribute(SST.OpCode, II.NewInstruction.OpCode.Name); } if (II.InstructionOperandPatch) { if (patchelem == null) { patchelem = xInstruction.CreateCompressedElement(SST.InstructionPatch); } xmlInstructionModifiedList.Operand2Node(patchelem, II.NewInstruction); } if (patchelem != null) { xInstruction.AppendChild(patchelem); } } } else { xInstruction.CreateAttribute(SST.InstructionNum, II.NewInstructionNum.ToString()); xInstruction.CreateAttribute(SST.OpCode, II.NewInstruction.OpCode.Name); if (PatchStatus != PatchStatus.Broken) // maby safer if == PatchStatus.WoringPerfectly { xmlInstructionModifiedList.Operand2Node(xInstruction, II.NewInstruction); } instructionPos++; } } return(true); }
/// <summary>Loads the patch data from an XmlNode</summary> /// <param name="input">The XmlNode containing the Instruction nodes</param> /// <returns>Returns true if it succeeded, false otherwise</returns> public override bool Load(XmlNode input) { NameCompressor nc = NameCompressor.Instance; Validator val = new Validator(() => PatchStatus = PatchStatus.Broken); if (!val.ValidateTrue(input.ChildNodes.Count > 0, $"Node {input.Name} has no Childnodes")) { return(val.Ok); } XmlNode PatchList = input.GetChildNode(SST.PatchList, 0); if (!val.ValidateSet(PatchList, "No PatchList Child found")) { return(val.Ok); } string metpathunres = PatchList.GetAttribute(SST.MethodPath); if (!val.ValidateStringSet(metpathunres, "MethodPath Attribute not found or empty")) { return(val.Ok); } methodDefinition = DataStruct.ReferenceTable.Resolve(metpathunres.ToBaseInt()) as MethodDefinition; if (!val.ValidateSet(methodDefinition, $"MethodID \"{metpathunres}\" couldn't be resolved")) { return(val.Ok); } // TODO: move methodDef related checks to Execute OriginalInstructionCount = int.Parse(PatchList.GetAttribute(SST.InstructionCount)); // if new method body has changed -> patching the new assembly will not work if (!val.ValidateTrue(methodDefinition.Body.Instructions.Count == OriginalInstructionCount, $"The PatchAction \" { Name }\" cannot be applied to a changend method")) { return(val.Ok); } // TODO: init with given params, instead of static AnyArray <InstructionInfo> iibuffer = new AnyArray <InstructionInfo>(OriginalInstructionCount); bool checkopcdes = true; bool resolveparams = false; // resolves types/methods/... bool checkprimitives = true; // checks if primitive types are identical var xmlInstructionLoader = new XMLInstruction <InstructionInfo>( DataStruct.ReferenceTable, methodDefinition); #region Load all InstructionInfo foreach (XmlNode xelem in PatchList.ChildNodes) { if (!val.ValidateTrue(xelem.Name == nc[SST.Instruction], $"PatchList elemtent \"{xelem.Name}\" is not recognized")) { continue; } InstructionInfo nII = new InstructionInfo(); XmlAttribute xdelatt = xelem.Attributes[nc[SST.Delete]]; OpCode opcode = ILManager.OpCodeLookup[xelem.GetAttribute(SST.OpCode)]; if (xdelatt != null) // Old_Instruction { nII.Delete = xdelatt.Value == nc[SST.True]; //Load old Instruction into InstructionInfo nII.OldInstructionNum = int.Parse(xelem.GetAttribute(SST.InstructionNum)); if (nII.OldInstructionNum < OriginalInstructionCount) { nII.OldInstruction = methodDefinition.Body.Instructions[nII.OldInstructionNum]; val.ValidateTrue(checkopcdes && opcode == nII.OldInstruction.OpCode, () => { nII.OpCodeMismatch = true; return($"Opcode of Instruction {nII.OldInstructionNum} has changed"); }); // TODO: set mismatch | from-to log } else { continue; } if (checkprimitives) { if (opcode.OperandType == nII.OldInstruction.OpCode.OperandType) { string oldPrimOperand; if (nII.OldInstruction.Operand == null) { nII.PrimitiveMismatch = xelem.GetAttribute(SST.PrimitiveValue, out oldPrimOperand); } else { nII.PrimitiveMismatch = xelem.GetAttribute(SST.PrimitiveValue) != nII.OldInstruction.Operand.ToString(); } } else { nII.PrimitiveMismatch = true; } } if (resolveparams) { Log.Write(Log.Level.Info, "Resolve not implemented yet."); //TODO: PatchStatus = PatchStatus.Broken; of not found ref implement, when DeepTypeCompare works } if (xelem.ChildNodes.Count == 1) // check if patchnode exists { XmlNode xpatchelem = xelem.ChildNodes[0]; string instnum = xpatchelem.GetAttribute(SST.InstructionNum); if (string.IsNullOrEmpty(instnum)) // check InstructionNum Patch { nII.NewInstructionNum = nII.OldInstructionNum; } else { nII.NewInstructionNum = int.Parse(instnum); } OpCode patchopc; string patchopcode = xpatchelem.GetAttribute(SST.OpCode); if (string.IsNullOrEmpty(patchopcode)) // check Opcode patch { patchopc = opcode; } else { patchopc = ILManager.OpCodeLookup[patchopcode]; } nII.NewInstruction = xmlInstructionLoader.Node2Instruction(xpatchelem, patchopc, nII.NewInstructionNum); if (nII.NewInstruction == null) { nII.NewInstruction = ILManager.GenInstruction(patchopc, nII.OldInstruction.Operand); } } else // no patchnode -> clone old one { nII.NewInstructionNum = nII.OldInstructionNum; nII.NewInstruction = nII.OldInstruction.Clone(); } } else // New_Instruction { nII.OldInstructionNum = -1; nII.NewInstructionNum = int.Parse(xelem.GetAttribute(SST.InstructionNum)); nII.NewInstruction = xmlInstructionLoader.Node2Instruction(xelem, opcode, nII.NewInstructionNum); val.ValidateSet(nII.NewInstruction, () => { nII.NewInstruction = Instruction.Create(OpCodes.Nop); nII.NewInstruction.OpCode = opcode; nII.NewInstruction.Operand = "!!!!! Dummy !!!!!"; return($"Expected Operand for '{opcode.Name}', but no matching Attribute was found in {xelem.InnerXml}"); }); } iibuffer[nII.NewInstructionNum] = nII; } #endregion instructPatchList = new List <InstructionInfo>(iibuffer.ToArray()); // Check for not loaded Instructions if (!val.ValidateTrue(instructPatchList.All(x => x != null), () => "PatchList has holes: " + string.Join(", ", instructPatchList.Select((b, i) => b == null ? i : -1).Where(i => i != -1).ToArray()))) { return(false); } #region Set all jump operands from PostInitData foreach (PostInitData pid in xmlInstructionLoader.GetPostInitalisationList()) { if (pid.isArray) { bool success = true; instructPatchList[pid.InstructionNum].NewInstruction.Operand = Array.ConvertAll(pid.targetArray, a => { InstructionInfo pidinstr = instructPatchList.FirstOrDefault(x => x.NewInstructionNum == a); if (!val.ValidateSet(pidinstr, () => { success = false; return($"PID_At: {a}"); })) { return(null); } else { return(pidinstr.NewInstruction); } }); if (!val.ValidateTrue(success, $"PostInitData failed: {pid}")) { continue; } } else { InstructionInfo pidinstr = instructPatchList.First(x => x.NewInstructionNum == pid.targetNum); if (!val.ValidateSet(pidinstr, $"PostInitData failed: {pid}")) { continue; } instructPatchList[pid.InstructionNum].NewInstruction.Operand = pidinstr.NewInstruction; } } #endregion if (PatchStatus == PatchStatus.Unset) { PatchStatus = PatchStatus.WoringPerfectly; } return(val.Ok); }