Ejemplo n.º 1
0
        public void ApplyPatches(List<PatchDefinition> patchDefinitions)
        {
            _appliedPatchInfos = new List<AppliedPatchInfo>(); //process this when saving the result

            foreach (var patchDefinition in patchDefinitions)
            {
                RemovePatches(
                    new List<Tuple<PatchDefinition, AppliedPatchInfo>>()
                        {
                        new Tuple<PatchDefinition, AppliedPatchInfo>( patchDefinition, _appliedPatchInfos.FirstOrDefault(a => a.Name == patchDefinition.Name))
                        }
                    ); //check if it has already been applied and remove beforehand

                var newlyAppliedPatch = new AppliedPatchInfo(patchDefinition.Name, patchDefinition.Version);
                _appliedPatchInfos.Add(newlyAppliedPatch);

                foreach (var variable in patchDefinition.Variables) //TODO: remove variable
                {
                    var targetMethod = CodeHelper.ResolveMethodDefinition(_assembly.MainModule, variable.TargetMethodName);

                    var targetType = TypeParser.ParseType(_assembly.MainModule, variable.Type);

                    if (targetType == null)
                    {
                        throw new ApplicationException(string.Format("Type {0} couldn't be resolved for variable {1}", variable.TargetMethodName, variable.Name));
                    }

                    if (targetMethod == null)
                    {
                        throw new ApplicationException(
                            string.Format("Method {0} couldn't be resolved for variable {1}", variable.TargetMethodName, variable.Name));
                    }
                    targetMethod.Body.Variables.Add( new VariableDefinition(variable.Name, targetType));
                }

                foreach (var codeBlock in patchDefinition.CodeBlocks)
                {
                    var codeBlockInfo = new AppliedCodeBlock()
                                            {

                                                InsertPos = codeBlock.InsertOffset,
                                                MethodName = codeBlock.TargetMethodName,
                                                LinesCount = codeBlock.Lines.Count(),
                                                OriginalLinesCount = codeBlock.TargetMethodInstructionCount
                                            };
                    newlyAppliedPatch.AppliedCodeBlocks.Add(codeBlockInfo);

                    var typeName = codeBlock.TargetMethodName.Split(new[] { " ", "::" }, 3, StringSplitOptions.RemoveEmptyEntries)[1];

                    var type = _assembly.MainModule.Types.FirstOrDefault(t => t.FullName == typeName);

                    if (type == null)
                    {
                        throw new ApplicationException(string.Format("type {0} not found in assembly {1}", typeName, _assemblyPath));
                    }

                    var method = type.Methods.FirstOrDefault(m => m.FullName == codeBlock.TargetMethodName);

                    if (method == null)
                    {
                        throw new ApplicationException(string.Format("method {0} not found in assembly: {1}", codeBlock.TargetMethodName,
                                                                     _assemblyPath));
                    }

                    if (codeBlock.TargetMethodInstructionCount < 0)
                    {
                        codeBlockInfo.OriginalLinesCount = method.Body.Instructions.Count;
                    }

                    //var processor = method.Body.GetILProcessor();

                    var insertAt = method.Body.Instructions.Select((i, idx) => new Tuple<Instruction, int>(i, idx)).FirstOrDefault(
                        it =>
                            it.Item1.Offset >= codeBlock.InsertOffset
                        );

                    var insertIdx = insertAt != null ? insertAt.Item2 : 0;

                    if (codeBlock.InsertOffset > 0 && insertIdx == 0)
                    {
                        Debug.WriteLine("something went wrong. ipos is:{0}", codeBlock.InsertOffset);
                    }

                    //Debug.Assert(codeBlock.InsertOffset != 163);

                    if (codeBlock.TargetMethodInstructionCount > 0 && codeBlock.TargetMethodInstructionCount != method.Body.Instructions.Count)
                    {
                        throw new ApplicationException(string.Format("Instruction count({0}) in the target method {1}  does not match the expected count {2}",
                                                                     method.Body.Instructions.Count,
                                                                     codeBlock.TargetMethodName,
                                                                     codeBlock.TargetMethodInstructionCount));
                    }

                    codeBlockInfo.InsertPos = insertIdx;
                    foreach (var code in codeBlock.Lines)
                    {
                        var i = code.Create(method.Body, _assembly.MainModule);

                        method.Body.Instructions.Insert(insertIdx++, i);

                    }
                }

                //UI patches
                foreach (var uiPatch in patchDefinition.UIPatches) //TODO: too phat, move out
                {
                    var uiPatcher = new UIDataLoader();

                    var targetPath = Path.Combine(_gameRoot, uiPatch.FilePath);

                    var doc = uiPatcher.FixAndLoadXml(targetPath);

                    var uiPatchBackupNodes = doc.XPathSelectElements(string.Format("//SotsosBackup/UIPatchBackup[@PatchName='{0}']", patchDefinition.Name));

                    var installed = uiPatchBackupNodes.Select(xElement => xElement.Attributes("XPath").FirstOrDefault()).Any(xpathAtt => xpathAtt.Value == uiPatch.XPath);
                    if (installed) //already installed, skip
                    {
                        continue;
                    }

                    var targetNode = doc.XPathSelectElement(uiPatch.XPath);

                    var xmlNodeReader = new XmlNodeReader(uiPatch.Data);
                    var newNode = XElement.Load(xmlNodeReader).Descendants().First();

                    var sotsosBackupNode = doc.XPathSelectElement("//SotsosBackup");

                    if (sotsosBackupNode == null)
                    {
                        sotsosBackupNode = new XElement("SotsosBackup");
                        doc.Root.DescendantNodes().Last().AddAfterSelf(sotsosBackupNode);
                    }

                    var uiPatchBackupNode = new XElement("UIPatchBackup");

                    uiPatchBackupNode.SetAttributeValue("XPath", uiPatch.XPath);
                    uiPatchBackupNode.SetAttributeValue("PatchMode", uiPatch.PatchMode);
                    uiPatchBackupNode.SetAttributeValue("PatchName", patchDefinition.Name);
                    uiPatchBackupNode.SetAttributeValue("PatchVersion", patchDefinition.Version);

                    sotsosBackupNode.Add(uiPatchBackupNode);

                    if (uiPatch.PatchMode == UIPatchMode.Replace)
                    {
                        var dataNode = new XElement("Data");
                        uiPatchBackupNode.Add(dataNode);

                        var parent = targetNode.Parent ?? doc.Root;
                        var prevNode = targetNode.PreviousNode;

                        targetNode.Remove();
                        dataNode.Add(targetNode);

                        if (prevNode != null)
                        {
                            prevNode.AddAfterSelf(newNode);
                        }
                        else
                        {
                            parent.Add(newNode); //allow the nullref to flow up
                        }
                    }
                    else if (uiPatch.PatchMode == UIPatchMode.InsertAfter)
                    {
                        uiPatchBackupNode.SetAttributeValue("RemoveXPath", uiPatch.RemoveXPath);
                        targetNode.AddAfterSelf(newNode);

                    }
                    else if (uiPatch.PatchMode == UIPatchMode.InsertBefore)
                    {
                        uiPatchBackupNode.SetAttributeValue("RemoveXPath", uiPatch.RemoveXPath);
                        targetNode.AddBeforeSelf(newNode);
                    }

                    using (var writer = XmlWriter.Create(targetPath, new XmlWriterSettings() { Indent = true }))
                    {
                        doc.WriteTo(writer);
                    }
                }
            }
        }
Ejemplo n.º 2
0
        public void RemovePatches(List<Tuple<PatchDefinition, AppliedPatchInfo>> toBeRemoved )
        {
            if (_currentActivePatchInfos != null && _currentActivePatchInfos.Count > 0)
            {
                foreach (var patchToBeRemoved in toBeRemoved)
                {
                    var patchInfo = patchToBeRemoved.Item2;
                        //_currentActivePatchInfos.FirstOrDefault(pi => pi.Name == patchInfo);
                    var patchDefinition = patchToBeRemoved.Item1;
                    if (patchInfo != null)
                    {
                        var appliedBlocks = new List<AppliedCodeBlock>(patchInfo.AppliedCodeBlocks);
                        appliedBlocks.Reverse();

                        foreach (var cb in appliedBlocks)
                        {
                            var typeName =cb.MethodName.Split(new[] { " ", "::" }, 3, StringSplitOptions.RemoveEmptyEntries)[1];
                            var type = _assembly.MainModule.Types.FirstOrDefault(t => t.FullName == typeName);

                            var method = type.Methods.FirstOrDefault(m => m.FullName == cb.MethodName);

            //                            method.Body.Variables.Add(new VariableDefinition("ret","System.String"));

                            if (method.Body.Instructions.Count() != cb.LinesCount + cb.OriginalLinesCount)
                            {
                                throw new ApplicationException(string.Format("Invalid patch state for code block {0}. Can't remove.", cb.Src));
                            }

                            var insertIdx = cb.InsertPos;

                            for (var p = 0; p < cb.LinesCount; p++)
                            {
                                method.Body.Instructions.RemoveAt(insertIdx); //check if it keeps the offsets intact
                            }

                            if (method.Body.Instructions.Count() != cb.OriginalLinesCount)
                            {
                                throw new ApplicationException(string.Format("Invalid patch state for code block {0}. Can't remove.", cb.Src));
                            }

                        }
                        _currentActivePatchInfos.Remove(patchInfo);
                        _assembly.CustomAttributes.Remove(patchInfo.Attr);
                    }

                    //remove ui parts
                    if (patchDefinition != null)
                    {
                        if (patchDefinition.Variables != null && patchDefinition.Variables.Count > 0)
                        {
                            foreach (var variable in patchDefinition.Variables)
                            {
                                var targetMethod = CodeHelper.ResolveMethodDefinition(_assembly.MainModule, variable.TargetMethodName);

                                var reverseVariables = new List<VariableDefinition>(targetMethod.Body.Variables);
                                reverseVariables.Reverse();

                                var findVar = reverseVariables.FirstOrDefault(v => v.VariableType.FullName == variable.Type);
                                if (findVar != null)
                                {
                                    targetMethod.Body.Variables.Remove(findVar); //remove last variable with given type
                                }
                            }
                        }

                        foreach (var uiPatch in patchDefinition.UIPatches) //TODO: load from assembly info instead of existing patches for better maint.
                        {
                            var uiPatcher = new UIDataLoader();

                            var targetPath = Path.Combine(_gameRoot, uiPatch.FilePath);

                            var doc = uiPatcher.FixAndLoadXml(targetPath);

                            var sotsosBackupNodes = doc.XPathSelectElements(string.Format("//SotsosBackup/UIPatchBackup[@PatchName='{0}']", patchDefinition.Name));

                            if (sotsosBackupNodes.Any())
                            {
                                foreach (var backupNode in sotsosBackupNodes)
                                {
                                    var patchMode = UIPatchMode.Replace;

                                    Enum.TryParse(backupNode.Attribute("PatchMode").Value, true, out patchMode);

                                    switch (patchMode)
                                    {
                                        case UIPatchMode.Replace:
                                            {
                                                var xpath = backupNode.Attribute("XPath").Value;

                                                var dataNode = backupNode.Descendants("Data").FirstOrDefault();
                                                if (dataNode != null && dataNode.Elements().Count() == 1)
                                                {
                                                    var restoreLocation = doc.XPathSelectElement(xpath);
                                                    if (restoreLocation != null)
                                                    {
                                                        restoreLocation.AddAfterSelf(dataNode.Elements().First());
                                                        restoreLocation.Remove();
                                                    }
                                                }
                                                break;
                                            }
                                        case UIPatchMode.InsertBefore:
                                        case UIPatchMode.InsertAfter:
                                            {
                                                var xpath = backupNode.Attribute("RemoveXPath").Value;
                                                var nodeToRemove = doc.XPathSelectElement(xpath);
                                                if (nodeToRemove != null)
                                                {
                                                    nodeToRemove.Remove();
                                                }
                                                break;
                                            }
                                    }

                                    backupNode.Remove();
                                }

                            }
                            using (var writer = XmlWriter.Create(targetPath, new XmlWriterSettings(){Indent = true}))
                            {
                                doc.WriteTo(writer);
                            }
                        }
                    }
                }
            }
        }