예제 #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()));
            }
        }