private static void BuildBsaPatch(string inBsaName, string outBsaName) { string outBSAFile = Path.ChangeExtension(outBsaName, ".bsa"); string outBSAPath = Path.Combine(_dirTTWMain, outBSAFile); string inBSAFile = Path.ChangeExtension(inBsaName, ".bsa"); string inBSAPath = Path.Combine(_dirFO3Data, inBSAFile); var renameDict = BuildRenameDict(outBsaName); Debug.Assert(renameDict != null); var patPath = Path.Combine(OutDir, Path.ChangeExtension(outBsaName, ".pat")); if (File.Exists(patPath)) { return; } var prefix = Path.Combine(InDir, "TTW Patches", outBsaName); using (var inBSA = new BSA(inBSAPath)) using (var outBSA = new BSA(outBSAPath)) { BsaDiff .CreateRenameQuery(inBSA, renameDict) .ToList(); // execute query var oldFiles = inBSA.SelectMany(folder => folder).ToList(); var newFiles = outBSA.SelectMany(folder => folder).ToList(); var newChkDict = FileValidation.FromBSA(outBSA); var joinedPatches = from patKvp in newChkDict join newBsaFile in newFiles on patKvp.Key equals newBsaFile.Filename select new { newBsaFile, file = patKvp.Key, patch = patKvp.Value }; var allJoinedPatches = joinedPatches.ToList(); var patchDict = new PatchDict(allJoinedPatches.Count); foreach (var join in allJoinedPatches) { var oldBsaFile = oldFiles.SingleOrDefault(file => file.Filename == join.file); Debug.Assert(oldBsaFile != null, "File not found: " + join.file); var oldChk = FileValidation.FromBSAFile(oldBsaFile); var newChk = join.patch; var oldFilename = oldBsaFile.Filename; if (oldFilename.StartsWith(TaleOfTwoWastelands.Properties.Resources.VoicePrefix)) { patchDict.Add(join.file, new Patch(newChk, null)); continue; } var patches = new List <PatchInfo>(); var md5OldStr = Util.GetMD5String(oldBsaFile.GetContents(true)); var md5NewStr = Util.GetMD5String(join.newBsaFile.GetContents(true)); var diffPath = Path.Combine(prefix, oldFilename + "." + md5OldStr + "." + md5NewStr + ".diff"); var usedPath = Path.ChangeExtension(diffPath, ".used"); if (File.Exists(usedPath)) { File.Move(usedPath, diffPath); //fixes moronic things } var altDiffs = Util.FindAlternateVersions(diffPath); if (altDiffs != null) { foreach (var altDiff in altDiffs) { var altDiffBytes = GetDiff(altDiff.Item1, Diff.SIG_LZDIFF41); patches.Add(new PatchInfo { Metadata = new FileValidation(altDiff.Item2, 0, FileValidation.ChecksumType.Md5), Data = altDiffBytes }); } } if (newChk != oldChk) { byte[] diffData = GetDiff(diffPath, Diff.SIG_LZDIFF41); var patchInfo = PatchInfo.FromOldDiff(diffData, oldChk); Debug.Assert(patchInfo.Data != null); patches.Add(patchInfo); } patchDict.Add(join.file, new Patch(newChk, patches.ToArray())); } using (var stream = File.OpenWrite(patPath)) patchDict.WriteAll(stream); } }
private static void BuildBsaPatch(string inBsaName, string outBsaName) { string outBSAFile = Path.ChangeExtension(outBsaName, ".bsa"); string outBSAPath = Path.Combine(_dirTTWMain, outBSAFile); string inBSAFile = Path.ChangeExtension(inBsaName, ".bsa"); string inBSAPath = Path.Combine(_dirFO3Data, inBSAFile); var renameDict = BuildRenameDict(outBsaName); Debug.Assert(renameDict != null); var patPath = Path.Combine(OutDir, Path.ChangeExtension(outBsaName, ".pat")); if (File.Exists(patPath)) return; var prefix = Path.Combine(InDir, "TTW Patches", outBsaName); using (var inBSA = new BSA(inBSAPath)) using (var outBSA = new BSA(outBSAPath)) { BsaDiff .CreateRenameQuery(inBSA, renameDict) .ToList(); // execute query var oldFiles = inBSA.SelectMany(folder => folder).ToList(); var newFiles = outBSA.SelectMany(folder => folder).ToList(); var newChkDict = FileValidation.FromBSA(outBSA); var joinedPatches = from patKvp in newChkDict join newBsaFile in newFiles on patKvp.Key equals newBsaFile.Filename select new { newBsaFile, file = patKvp.Key, patch = patKvp.Value, }; var allJoinedPatches = joinedPatches.ToList(); var patchDict = new PatchDict(allJoinedPatches.Count); foreach (var join in allJoinedPatches) { var oldBsaFile = oldFiles.SingleOrDefault(file => file.Filename == join.file); Debug.Assert(oldBsaFile != null, "File not found: " + join.file); var oldChk = FileValidation.FromBSAFile(oldBsaFile); var newChk = join.patch; var oldFilename = oldBsaFile.Filename; if (oldFilename.StartsWith(Game.VoicePrefix)) { patchDict.Add(join.file, new Patch(newChk, null)); continue; } var patches = new List<PatchInfo>(); var md5OldStr = Util.GetMD5String(oldBsaFile.GetContents(true)); var md5NewStr = Util.GetMD5String(join.newBsaFile.GetContents(true)); var diffPath = Path.Combine(prefix, oldFilename + "." + md5OldStr + "." + md5NewStr + ".diff"); var usedPath = Path.ChangeExtension(diffPath, ".used"); if (File.Exists(usedPath)) File.Move(usedPath, diffPath); //fixes moronic things var altDiffs = Util.FindAlternateVersions(diffPath); if (altDiffs != null) { foreach (var altDiff in altDiffs) { var altDiffBytes = GetDiff(altDiff.Item1, Diff.SIG_LZDIFF41); patches.Add(new PatchInfo { Metadata = new FileValidation(altDiff.Item2, 0, FileValidation.ChecksumType.Md5), Data = altDiffBytes }); } } if (newChk != oldChk) { byte[] diffData = GetDiff(diffPath, Diff.SIG_LZDIFF41); var patchInfo = PatchInfo.FromOldDiff(diffData, oldChk); Debug.Assert(patchInfo.Data != null); patches.Add(patchInfo); } patchDict.Add(join.file, new Patch(newChk, patches.ToArray())); } using (var stream = File.OpenWrite(patPath)) patchDict.WriteAll(stream); } }
public bool PatchBsa(CompressionOptions bsaOptions, string oldBSA, string newBSA, bool simulate = false) { var Op = new InstallStatus(Progress, Token) { ItemsTotal = 7 }; var outBsaFilename = Path.GetFileNameWithoutExtension(newBSA); BSA bsa; try { Op.CurrentOperation = "Opening " + Path.GetFileName(oldBSA); bsa = new BSA(oldBSA, bsaOptions); } finally { Op.Step(); } IDictionary<string, string> renameDict; try { Op.CurrentOperation = "Opening rename database"; #if LEGACY var renamePath = Path.Combine(Installer.PatchDir, outBsaFilename, "RenameFiles.dict"); #else var renamePath = Path.Combine(Installer.PatchDir, Path.ChangeExtension(outBsaFilename, ".ren")); #endif if (File.Exists(renamePath)) { #if LEGACY renameDict = new Dictionary<string, string>(Util.ReadOldDatabase(renamePath)); #else using (var fileStream = File.OpenRead(renamePath)) using (var lzmaStream = new LzmaDecodeStream(fileStream)) using (var reader = new BinaryReader(lzmaStream)) { var numPairs = reader.ReadInt32(); renameDict = new Dictionary<string, string>(numPairs); while (numPairs-- > 0) renameDict.Add(reader.ReadString(), reader.ReadString()); } #endif } else renameDict = new Dictionary<string, string>(); } finally { Op.Step(); } PatchDict patchDict; try { Op.CurrentOperation = "Opening patch database"; #if LEGACY var chkPrefix = Path.Combine(Installer.PatchDir, outBsaFilename); var chkPath = Path.Combine(chkPrefix, "CheckSums.dict"); patchDict = PatchDict.FromOldDatabase(Util.ReadOldDatabase(chkPath), chkPrefix, b => b); #else var patchPath = Path.Combine(Installer.PatchDir, Path.ChangeExtension(outBsaFilename, ".pat")); if (File.Exists(patchPath)) { patchDict = new PatchDict(patchPath); } else { Log.Dual("\tNo patch database is available for: " + oldBSA); return false; } #endif } finally { Op.Step(); } using (bsa) { try { RenameFiles(bsa, renameDict); if (renameDict.Count > 0) { foreach (var kvp in renameDict) { Log.Dual("File not found: " + kvp.Value); Log.Dual("\tCannot create: " + kvp.Key); } } } finally { Op.Step(); } var allFiles = bsa.SelectMany(folder => folder).ToList(); try { var opChk = new InstallStatus(Progress, Token) { ItemsTotal = patchDict.Count }; var joinedPatches = from patKvp in patchDict //if the join is not grouped, this will exclude missing files, and we can't find and fail on them join oldFile in allFiles on patKvp.Key equals oldFile.Filename into foundOld join bsaFile in allFiles on patKvp.Key equals bsaFile.Filename select new PatchJoin(bsaFile, foundOld.SingleOrDefault(), patKvp.Value); #if DEBUG var watch = new Stopwatch(); try { watch.Start(); #endif #if PARALLEL Parallel.ForEach(joinedPatches, join => #else foreach (var join in joinedPatches) #endif HandleFile(opChk, join) #if PARALLEL ) #endif ; #if DEBUG } finally { watch.Stop(); Debug.WriteLine(outBsaFilename + " HandleFile loop finished in " + watch.Elapsed); } #endif } finally { Op.Step(); } try { Op.CurrentOperation = "Removing unnecessary files"; var notIncluded = allFiles.Where(file => !patchDict.ContainsKey(file.Filename)); var filesToRemove = new HashSet<BSAFile>(notIncluded); foreach (BSAFolder folder in bsa) folder.RemoveWhere(filesToRemove.Contains); var emptyFolders = bsa.Where(folder => folder.Count == 0).ToList(); emptyFolders.ForEach(folder => bsa.Remove(folder)); } finally { Op.Step(); } try { Op.CurrentOperation = "Saving " + Path.GetFileName(newBSA); if (!simulate) bsa.Save(newBSA.ToLowerInvariant()); } finally { Op.Step(); } } Op.Finish(); return true; }
public static Dictionary <string, FileValidation> FromBSA(BSA bsa) { return(bsa .SelectMany(folder => folder) .ToDictionary(file => file.Filename, file => FromBSAFile(file))); }