static void patchFile(string origPath, string patchedPath, string diffFile) { List <Patch> patches; using (StreamReader sr = new StreamReader(diffFile)) { XmlSerializer xs = new XmlSerializer(typeof(List <Patch>)); patches = (List <Patch>)xs.Deserialize(sr); } byte[] orig = File.ReadAllBytes(origPath); byte[] mod = new byte[orig.Length]; Array.Copy(orig, mod, orig.Length); int cApplied = 0, cTodo = 0; int patchNum = 0; foreach (Patch p in patches) { Console.WriteLine(); patchNum++; int manualLoc = 0; if (g_overrides.ContainsKey(patchNum)) { manualLoc = g_overrides[patchNum]; } List <int> matchLoc = new List <int>(); byte[] match = p.matchBytes; for (int i = 0; i < orig.Length - match.Length; ++i) { bool mismatch = false; for (int j = 0; j < match.Length; ++j) { if (orig[i + j] != match[j]) { mismatch = true; break; } } if (mismatch) { continue; } matchLoc.Add(i); } int applyAtOffset = -1; int cMatches = matchLoc.Count; if (cMatches == 1) { applyAtOffset = matchLoc[0] + p.patchOffset; Console.WriteLine("<<<{0}>>> 100% MATCH: {1} bytes at 0x{2:X8}\n{3}", patchNum, p.patchBytes.Length, matchLoc[0] + p.patchOffset, Useful.PatchDetails(p)); } else if (cMatches > 1) { Console.WriteLine("<<<{0}>>> MULTIPLE MATCHES:\n{1} matches q={2:p0}\n{3}", patchNum, cMatches, p.quality, Useful.PatchDetails(p)); const int NFirst = 5; Console.WriteLine("FIRST {0} MATCHES", Math.Min(NFirst, cMatches)); for (int i = 0; i < Math.Min(cMatches, NFirst); ++i) { Console.WriteLine("#{0} at 0x{1:X8}", i + 1, matchLoc[i] + p.patchOffset); } if (manualLoc != 0) { if (manualLoc > 0) { applyAtOffset = manualLoc; } else { applyAtOffset = matchLoc[-1 - manualLoc] + p.patchOffset; } } } else { Console.WriteLine("<<<{0}>>> NO EXACT MATCHES FOUND\n{1}", patchNum, Useful.PatchDetails(p)); // weighted matching: score every half-byte matching; patch bytes weigh more than surroundings. Find top scores, print top5 and their relative and absolute weights. Dictionary <int, double> scores = new Dictionary <int, double>(orig.Length - match.Length); double[] matchWeight = new double[match.Length]; double matchWeightNormalizer = 0.0; for (int i = 0; i < match.Length; ++i) { double weight; if (i < p.patchOffset) { weight = 1.0 / Math.Sqrt(p.patchOffset - i + 1); } else if (i < p.patchOffset + p.patchBytes.Length) { weight = 1.0; } else { weight = 1.0 / Math.Sqrt(i + 2 - (p.patchOffset + p.patchBytes.Length)); } matchWeight[i] = weight; matchWeightNormalizer += weight; } for (int i = 0; i < match.Length; ++i) { matchWeight[i] /= matchWeightNormalizer; } for (int i = 0; i < orig.Length - match.Length; ++i) { double score = 0.0; for (int j = 0; j < match.Length; ++j) { byte o = orig[i + j]; byte m = match[j]; double factor; int x = o ^ m; if (x == 0) { factor = 1.0; } else if (x == 0xF0 || x == 0x0F) { factor = .3; } else { continue; } score += factor * matchWeight[j]; } scores.Add(i, score); } const int NTop = 5; var topN = scores.OrderByDescending(x => x.Value).Take(NTop).ToArray(); byte[] topMatchBytes = orig.Skip(topN[0].Key).Take(p.matchBytes.Length).ToArray(); Patch tmp = new Patch(topMatchBytes, p.patchBytes, p.patchOffset, 0); bool fApplyBestBet = manualLoc == 0 && topN[0].Value * 100 >= g_confidence; Console.WriteLine("TOP {0} MATCHES: TOP 1 is {1:P0}\n{2}", NTop, topN[0].Value, Useful.PatchDetails(tmp)); for (int i = 0; i < NTop; ++i) { var x = topN[i]; Console.WriteLine("#{0}: {1:P0} at 0x{2:X8})", i + 1, x.Value, x.Key + p.patchOffset); } if (fApplyBestBet) { applyAtOffset = topN[0].Key + p.patchOffset; } else if (manualLoc != 0) { if (manualLoc > 0) { applyAtOffset = manualLoc; } else { applyAtOffset = topN[-1 - manualLoc].Key + p.patchOffset; } } } if (applyAtOffset > 0) { Console.WriteLine( "------------------------------\n" + "++APPLIED at 0x{0:X8}", applyAtOffset); Array.Copy(p.patchBytes, 0, mod, applyAtOffset, p.patchBytes.Length); cApplied++; } else { Console.WriteLine( "------------------------------\n" + "!!! TODO #{0} !!!", ++cTodo); } } File.WriteAllBytes(patchedPath, mod); Console.WriteLine( "\n=============================================\n" + "Applied {0} of {1} patches ({2:P0})", cApplied, patches.Count, ((double)cApplied) / patches.Count); }
static void diffFiles(string origPath, string modPath, string diffFile) { byte[] orig = File.ReadAllBytes(origPath); byte[] mod = File.ReadAllBytes(modPath); int commonSize = Math.Min(orig.Length, mod.Length); int chunkStart = -1; int chunkEnd = -1; bool inChunk = false; bool previouslyInChunk = false; List <Patch> patches = new List <Patch>(); for (int i = 0; i < commonSize; ++i) { inChunk = orig[i] != mod[i]; if (previouslyInChunk != inChunk) { previouslyInChunk = inChunk; if (inChunk) { chunkStart = i; } else { chunkEnd = i; patches.Add(processChunk(orig, mod, chunkStart, chunkEnd)); } } } Debug.Assert(!inChunk); Console.WriteLine("{0} patches located", patches.Count); foreach (Patch p in patches) { Console.WriteLine("{0} bytes, {1:P0} quality:\t{2}", p.matchBytes.Length, p.quality, Useful.MatchDetails(p)); } using (StreamWriter sw = new StreamWriter(diffFile)) { XmlSerializer xs = new XmlSerializer(typeof(List <Patch>)); xs.Serialize(sw, patches); sw.Flush(); } }