Пример #1
0
        /// <summary>
        /// This method replaces the bodies of methods with ModifiesMember() with the original bodies, from the modified members. This lets you merge any changes in the game code with your mod code.
        /// </summary>
        /// <param name="manifest"></param>
        /// <param name="overwriteExistingSource">If true, the existing files will be modified, instead of the source.</param>
        public static void ReplaceCodeWithOriginal(PatchingManifest manifest, bool overwriteExistingSource = false)
        {
            var methodActions = manifest.MethodActions[typeof(ModifiesMemberAttribute)];
            var changeEntries =
                from methodAction in methodActions
                let yourMember = methodAction.YourMember
                                 let change = yourMember.Body
                                              select new {
                Start          = change.Instructions.First(x => x.SequencePoint != null).SequencePoint,
                End            = change.Instructions.First(x => x.SequencePoint != null).SequencePoint,
                Name           = yourMember.Name,
                ModifiedMember = methodAction.TargetMember
            };

            foreach (var byFile in changeEntries.GroupBy(x => x.Start.Document.Url))
            {
                var newName = overwriteExistingSource ? byFile.Key : Path.ChangeExtension(byFile.Key, ".new.cs");

                var contents   = File.ReadAllLines(byFile.Key);
                var linkedList = new LinkedList <char>();
                var lineNodes  = new List <LinkedListNode <char> >();
                foreach (var line in contents)
                {
                    var curLine = linkedList.AddLastRange(line + "\r\n");
                    lineNodes.Add(curLine);
                }

                var fileChanges = byFile.OrderBy(x => x.Start.StartLine).ToList();

                for (int index = 0; index < fileChanges.Count; index++)
                {
                    //basically, we're selecting all of the text from the first method instruction to the last
                    //and replacing it with the decompiled text of the original method. We exactly preserves the structure
                    //(even the brace style and indentation), so the diff tools will know exactly what to compare with what.
                    var change    = fileChanges[index];
                    var startNode = lineNodes[change.Start.StartLine - 1].Skip(change.Start.StartColumn - 1);
                    var preStart  = startNode.Previous;
                    var endNode   = lineNodes[change.End.EndLine - 1].Skip(change.End.EndColumn - 1);

                    var decompiledSource = Decompile(change.Name, change.ModifiedMember);
                    var lines            = decompiledSource.Split(new[] {
                        '\r',
                        '\n'
                    }, StringSplitOptions.RemoveEmptyEntries).ToList();
                    lines.RemoveAt(0);                     //remove the method signature
                    //find the indent of the current level:
                    var indent =
                        lineNodes[change.Start.StartLine - 1].NextNodes().TakeWhile(node => node.Value == '\t' || node.Value == ' ')
                        .Aggregate("",
                                   (acc, cur) => acc + cur.Value);
                    //add the indent to every level except for the first:
                    decompiledSource = lines.Select((line, i) => i == 0 ? line : indent + line).Join("\r\n");
                    //replace the decompiled body with the existing body:
                    linkedList.RemoveRange(startNode, endNode);
                    linkedList.AddAfterRange(preStart, decompiledSource);
                }
                File.WriteAllText(newName, new string(linkedList.ToArray()));
            }
        }
Пример #2
0
        /// <summary>
        /// This method replaces the bodies of methods with ModifiesMember() with the original bodies, from the modified members. This lets you merge any changes in the game code with your mod code.
        /// </summary>
        /// <param name="manifest"></param>
        /// <param name="overwriteExistingSource">If true, the existing files will be modified, instead of the source.</param>
        public static void ReplaceCodeWithOriginal(PatchingManifest manifest, bool overwriteExistingSource = false)
        {
            var methodActions = manifest.MethodActions[typeof (ModifiesMemberAttribute)];
            var changeEntries =
                from methodAction in methodActions
                let yourMember = methodAction.YourMember
                let change = yourMember.Body
                select new {
                    Start = change.FirstSequencePoint(),
                    End = change.LastSequencePoint(),
                    Name = yourMember.Name,
                    ModifiedMember = methodAction.TargetMember
                };
            foreach (var byFile in changeEntries.GroupBy(x => x.Start.Document.Url)) {
                var newName = overwriteExistingSource ? byFile.Key : Path.ChangeExtension(byFile.Key, ".new.cs");

                var contents = File.ReadAllLines(byFile.Key);
                var linkedList = new LinkedList<char>();
                var lineNodes = new List<LinkedListNode<char>>();
                foreach (var line in contents) {
                    var curLine = linkedList.AddLastRange(line + "\r\n");
                    lineNodes.Add(curLine);
                }

                var fileChanges = byFile.OrderBy(x => x.Start.StartLine).ToList();

                for (int index = 0; index < fileChanges.Count; index++) {
                    //basically, we're selecting all of the text from the first method instruction to the last
                    //and replacing it with the decompiled text of the original method. We exactly preserves the structure
                    //(even the brace style and indentation), so the diff tools will know exactly what to compare with what.
                    var change = fileChanges[index];
                    var startNode = lineNodes[change.Start.StartLine - 1].Skip(change.Start.StartColumn - 1);
                    var preStart = startNode.Previous;
                    var endNode = lineNodes[change.End.EndLine - 1].Skip(change.End.EndColumn - 1);

                    var decompiledSource = Decompile(change.Name, change.ModifiedMember);
                    var lines = decompiledSource.Split(new[] {
                        '\r',
                        '\n'
                    }, StringSplitOptions.RemoveEmptyEntries).ToList();
                    lines.RemoveAt(0); //remove the method signature
                    //find the indent of the current level:
                    var indent =
                        lineNodes[change.Start.StartLine - 1].NextNodes().TakeWhile(node => node.Value == '\t' || node.Value == ' ')
                            .Aggregate("",
                                (acc, cur) => acc + cur.Value);
                    //add the indent to every level except for the first:
                    decompiledSource = lines.Select((line, i) => i == 0 ? line : indent + line).Join("\r\n");
                    //replace the decompiled body with the existing body:
                    linkedList.RemoveRange(startNode, endNode);
                    linkedList.AddAfterRange(preStart, decompiledSource);
                }
                File.WriteAllText(newName, new string(linkedList.ToArray()));
            }
        }
Пример #3
0
        /// <exception cref="T:Patchwork.Engine.PatchDeclerationException">The patch did not have a PatchInfo class.</exception>
        public static PatchingManifest TryGetManifest(string assemblyPath)
        {
            PatchingManifest patchingManifest = ManifestCreator.CreateManifest(assemblyPath);

            if (patchingManifest.PatchInfo == null)
            {
                throw new PatchDeclerationException("The patch did not have a PatchInfo class.");
            }

            return(patchingManifest);
        }
Пример #4
0
        public PatchInstruction Command_Direct_AddPatch(string path, bool isEnabled)
        {
            var targetPath = path;
            var fileName   = Path.GetFileName(path);

            var hadToCopy             = false;
            PatchingManifest manifest = null;

            try
            {
                Directory.CreateDirectory(_modFolder);
                var folder         = Path.GetDirectoryName(path);
                var absoluteFolder = PathHelper.GetAbsolutePath(folder);
                var modsPath       = PathHelper.GetAbsolutePath(_modFolder);

                if (!Preferences.DontCopyFiles &&
                    !modsPath.Equals(absoluteFolder, StringComparison.InvariantCultureIgnoreCase))
                {
                    targetPath = Path.Combine(_modFolder, fileName);
                    File.Copy(path, targetPath, true);
                    hadToCopy = true;
                }
                manifest = ManifestMaker.CreateManifest(PathHelper.GetAbsolutePath(targetPath));
                if (manifest.PatchInfo == null)
                {
                    throw new PatchDeclerationException("The patch did not have a PatchInfo class.");
                }
                var patchInstruction = new PatchInstruction
                {
                    IsEnabled             = isEnabled,
                    Patch                 = manifest,
                    PatchLocation         = PathHelper.GetRelativePath(targetPath),
                    AppInfo               = AppInfo,
                    PatchOriginalLocation = path
                };
                Instructions.Add(patchInstruction);
                return(patchInstruction);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"The patch located in {path} could not be loaded.");
                manifest?.Dispose();
                if (hadToCopy)
                {
                    File.Delete(targetPath);
                }
                throw;
            }
        }
Пример #5
0
        private static void TryAddPatch(string assemblyPath)
        {
            var manifest = new PatchingManifest();

            try
            {
                manifest = TryGetManifest(assemblyPath);
            }
            catch (PatchDeclerationException patchDeclerationException)
            {
                Logger.Error(patchDeclerationException, "Cannot load patch assembly: {0}", assemblyPath);
                manifest?.Dispose();
                return;
            }

            AddXmlInstruction(assemblyPath, manifest);
        }
Пример #6
0
        private static void AddXmlInstruction(string assemblyPath, PatchingManifest patchingManifest, bool enabled = true)
        {
            List <XmlInstruction> instructions = SettingsManager.XmlData.Instructions;

            Logger.Debug("Adding patch instruction: {0}", assemblyPath);

            var xmlInstruction = new XmlInstruction
            {
                IsEnabled = true,
                Name      = patchingManifest.PatchInfo.PatchName,
                Location  = assemblyPath
            };

            instructions.Add(xmlInstruction);

            SettingsManager.XmlData.Instructions = instructions;
        }
Пример #7
0
 public static PatchInstruction Configure(this PatchInstruction patchInstruction, AppInfo context, PatchingManifest manifest, string location, bool enabled = true)
 {
     patchInstruction.IsEnabled = enabled;
     patchInstruction.Patch     = manifest;
     patchInstruction.Location  = location;
     patchInstruction.AppInfo   = context;
     return(patchInstruction);
 }