/// <summary> /// Checks 00.dat files, indcluding fpk contents and adds the different mod entry types (if missing) to database (snakebite.xml) /// Slows down as number of fpks increase /// </summary> public static void CleanupDatabase() { Debug.LogLine("[Cleanup] Database cleanup started", Debug.LogLevel.Basic); // Retrieve installation data SettingsManager manager = new SettingsManager(SnakeBiteSettings); var mods = manager.GetInstalledMods(); var game = manager.GetGameData(); var zeroFiles = GzsLib.ListArchiveContents<QarFile>(ZeroPath); //Should only happen if user manually mods 00.dat Debug.LogLine("[Cleanup] Removing duplicate file entries", Debug.LogLevel.Debug); // Remove duplicate file entries var cleanFiles = zeroFiles.ToList(); foreach (string file in zeroFiles) { while (cleanFiles.Count(entry => entry == file) > 1) { cleanFiles.Remove(file); Debug.LogLine(String.Format("[Cleanup] Found duplicate file in 00.dat: {0}", file), Debug.LogLevel.Debug); } } Debug.LogLine("[Cleanup] Examining FPK archives", Debug.LogLevel.Debug); var GameFpks = game.GameFpkEntries.ToList(); // Search for FPKs in game data var fpkFiles = cleanFiles.FindAll(entry => entry.EndsWith(".fpk") || entry.EndsWith(".fpkd")); foreach (string fpkFile in fpkFiles) { string fpkName = Path.GetFileName(fpkFile); // Extract FPK from archive Debug.LogLine(String.Format("[Cleanup] Examining {0}", fpkName)); GzsLib.ExtractFile<QarFile>(ZeroPath, fpkFile, fpkName); // Read FPK contents var fpkContent = GzsLib.ListArchiveContents<FpkFile>(fpkName); // Add contents to game FPK list foreach (var c in fpkContent) { if (GameFpks.Count(entry => Tools.CompareNames(entry.FilePath, c) && Tools.CompareHashes(entry.FpkFile, fpkFile)) == 0) { GameFpks.Add(new ModFpkEntry() { FpkFile = fpkFile, FilePath = c }); } } try { File.Delete(fpkName); } catch { Console.WriteLine("[Uninstall] Could not delete: " + fpkName); } } Debug.LogLine("[Cleanup] Checking installed mods", Debug.LogLevel.Debug); Debug.LogLine("[Cleanup] Removing all installed mod data from game data list", Debug.LogLevel.Debug); foreach (var mod in mods) { foreach (var qarEntry in mod.ModQarEntries) { cleanFiles.RemoveAll(file => Tools.CompareHashes(file, qarEntry.FilePath)); } foreach (var fpkEntry in mod.ModFpkEntries) { GameFpks.RemoveAll(fpk => Tools.CompareHashes(fpk.FpkFile, fpkEntry.FpkFile) && Tools.ToQarPath(fpk.FilePath) == Tools.ToQarPath(fpkEntry.FilePath)); } } Debug.LogLine("[Cleanup] Checking mod QAR files against game files", Debug.LogLevel.Debug); foreach (var s in cleanFiles) { if (game.GameQarEntries.Count(entry => Tools.CompareHashes(entry.FilePath, s)) == 0) { Debug.LogLine($"[Cleanup] Adding missing {s}", Debug.LogLevel.Debug); game.GameQarEntries.Add(new ModQarEntry() { FilePath = Tools.ToQarPath(s), SourceType = FileSource.System, Hash = Tools.NameToHash(s) }); } } game.GameFpkEntries = GameFpks; manager.SetGameData(game); }
private static void UnmergePackFiles(List <ModQarEntry> partialEditQarEntries, List <ModFpkEntry> partialRemoveFpkEntries) { GameData gameData = SBBuildManager.GetGameData(); List <ModFpkEntry> addedRepairFpkEntries = new List <ModFpkEntry>(); foreach (ModQarEntry partialEditQarEntry in partialEditQarEntries) { // create a list of fpk filepaths that need to be modified for the specific qar file (either restored to vanilla or removed from the pack) List <string> fpkPathsForThisQar = partialRemoveFpkEntries.Where(entry => entry.FpkFile == partialEditQarEntry.FilePath).Select(fpkEntry => Tools.ToWinPath(fpkEntry.FilePath)).ToList(); var fpkReferences = new List <string>();//tex references in fpk that need to be preserved/transfered to the rebuilt fpk // pull the vanilla qar file from the game archive, send to _gameFpk folder string winQarEntryPath = Tools.ToWinPath(partialEditQarEntry.FilePath); string gameQarPath = Path.Combine("_gameFpk", winQarEntryPath); if (partialEditQarEntry.SourceName != null) { string vanillaArchivePath = Path.Combine(GameDir, "master\\" + partialEditQarEntry.SourceName); //Debug.LogLine(string.Format("Pulling {0} from {1}", partialRemoveQarEntry.FilePath, partialRemoveQarEntry.SourceName)); GzsLib.ExtractFileByHash <QarFile>(vanillaArchivePath, partialEditQarEntry.Hash, gameQarPath); fpkReferences = GzsLib.GetFpkReferences(gameQarPath); } // pull the modded qar file from _working0 (assumed to already exist when the uninstall process reads 00.dat), send to _build folder string workingZeroQarPath = Path.Combine("_working0", winQarEntryPath); List <string> moddedFpkFiles = GzsLib.ExtractArchive <FpkFile>(workingZeroQarPath, "_build"); // split the fpk paths for this Qar into two categories: List <string> repairFilePathList = new List <string>(); // files that need to be repaired (aka overwritten by a vanilla file) if (partialEditQarEntry.SourceName != null) { repairFilePathList = GzsLib.ListArchiveContents <FpkFile>(gameQarPath).Intersect(fpkPathsForThisQar).ToList(); } List <string> removeFilePathList = fpkPathsForThisQar.Except(repairFilePathList).ToList(); // files that need to be removed (i.e. files that were non-native to the vanilla Qar) foreach (string repairFilePath in repairFilePathList) { string fpkBuildPath = Path.Combine("_build", repairFilePath); Debug.LogLine(string.Format("[Unmerge Fpk] Extracting repair file: {0}", repairFilePath), Debug.LogLevel.Basic); GzsLib.ExtractFile <FpkFile>(gameQarPath, repairFilePath, fpkBuildPath); // overwrites modded fpk files ModFpkEntry repairEntry = new ModFpkEntry { FpkFile = partialEditQarEntry.FilePath, FilePath = repairFilePath, // this will be a window path SourceType = FileSource.Merged, SourceName = partialEditQarEntry.SourceName }; gameData.GameFpkEntries.Add(repairEntry); addedRepairFpkEntries.Add(repairEntry); } var buildFiles = moddedFpkFiles.Except(removeFilePathList).ToList(); //tex refs werent grabbed from vanilla (or there weren't any in there in that case we're probably doubling the work lol) if (fpkReferences.Count == 0) { fpkReferences = GzsLib.GetFpkReferences(workingZeroQarPath); } GzsLib.WriteFpkArchive(workingZeroQarPath, "_build", buildFiles, fpkReferences); // writes the pack back to _working folder (leaving out the non-native fpk files) foreach (string removeFilePath in removeFilePathList) { int indexToRemove = gameData.GameFpkEntries.FindIndex(entry => entry.FilePath == removeFilePath); if (indexToRemove >= 0) { gameData.GameFpkEntries.RemoveAt(indexToRemove); } } } List <ModEntry> installedMods = SBBuildManager.GetInstalledMods(); foreach (ModEntry installedMod in installedMods) { List <string> qarPathsFound = new List <string>(); foreach (ModFpkEntry addedRepairEntry in addedRepairFpkEntries) { if (installedMod.ModQarEntries.FirstOrDefault(entry => entry.FilePath == addedRepairEntry.FpkFile) == null) { continue; } //Debug.LogLine(string.Format("checking {0} for {1} of {2}", installedMod.Name, addedRepairEntry.FilePath, addedRepairEntry.FpkFile)); if (installedMod.ModFpkEntries.RemoveAll(entry => entry.FilePath == Tools.ToQarPath(addedRepairEntry.FilePath) && entry.FpkFile == addedRepairEntry.FpkFile) > 0) { //Debug.LogLine(string.Format("found {0} of {1} in {2}", addedRepairEntry.FilePath, addedRepairEntry.FpkFile, installedMod.Name)); if (!qarPathsFound.Contains(addedRepairEntry.FpkFile)) { qarPathsFound.Add(addedRepairEntry.FpkFile); } } } foreach (string qarPathFound in qarPathsFound) { if (installedMod.ModFpkEntries.FirstOrDefault(entry => entry.FpkFile == qarPathFound) == null) //when the duplicate fpk file(s) were removed, there was nothing left in the modded qar. { //Debug.LogLine(string.Format("Removing {0} from {1}", qarPathFound, installedMod.Name)); installedMod.ModQarEntries.RemoveAll(entry => entry.FilePath == qarPathFound); // filters the qar file out of the list } } } SBBuildManager.SetInstalledMods(installedMods); SBBuildManager.SetGameData(gameData); }
public static bool ModifyFoxfs() // edits the chunk/texture lines in foxfs.dat to accommodate a_chunk7 a_texture7, MGO and GZs data. { CleanupFolders(); Debug.LogLine("[ModifyFoxfs] Beginning foxfs.dat check.", Debug.LogLevel.Debug); try { string foxfsInPath = "foxfs.dat"; string foxfsOutPath = "_extr\\foxfs.dat"; if (GzsLib.ExtractFile<QarFile>(chunk0Path, foxfsInPath, foxfsOutPath)) //extract foxfs alone, to save time if the changes are already made { if (!File.ReadAllText(foxfsOutPath).Contains("a_chunk7.dat")) // checks if there's an indication that it's modified { Debug.LogLine("[ModifyFoxfs] foxfs.dat is unmodified, extracting chunk0.dat.", Debug.LogLevel.Debug); List<string> chunk0Files = GzsLib.ExtractArchive<QarFile>(chunk0Path, "_extr"); //extract chunk0 into _extr string[] linesToAdd = new string[8] { " <chunk id=\"0\" label=\"old\" qar=\"a_chunk7.dat\" textures=\"a_texture7.dat\"/>", " <chunk id=\"1\" label=\"cypr\" qar=\"chunk0.dat\" textures=\"texture0.dat\"/>", " <chunk id=\"2\" label=\"base\" qar=\"chunk1.dat\" textures=\"texture1.dat\"/>", " <chunk id=\"3\" label=\"afgh\" qar=\"chunk2.dat\" textures=\"texture2.dat\"/>", " <chunk id=\"4\" label=\"mtbs\" qar=\"chunk3.dat\" textures=\"texture3.dat\"/>", " <chunk id=\"5\" label=\"mafr\" qar=\"chunk4.dat\" textures=\"texture4.dat\"/>", " <chunk id=\"6\" label=\"mgo\" qar=\"chunk5_mgo0.dat\" textures=\"texture5_mgo0.dat\"/>", " <chunk id=\"7\" label=\"gzs\" qar=\"chunk6_gzs0.dat\" textures=\"texture6_gzs0.dat\"/>", }; Debug.LogLine("[ModifyFoxfs] Updating foxfs.dat", Debug.LogLevel.Debug); var foxfsLine = File.ReadAllLines(foxfsOutPath).ToList(); // read the file int startIndex = foxfsLine.IndexOf(" <chunk id=\"0\" label=\"cypr\" qar=\"chunk0.dat\" textures=\"texture0.dat\"/>"); foxfsLine.RemoveRange(startIndex, 6); foxfsLine.InsertRange(startIndex, linesToAdd); File.WriteAllLines(foxfsOutPath, foxfsLine); // write to file Debug.LogLine("[ModifyFoxfs] repacking chunk0.dat", Debug.LogLevel.Debug); //Build chunk0.dat.SB_Build with modified foxfs GzsLib.WriteQarArchive(chunk0Path + build_ext, "_extr", chunk0Files, GzsLib.chunk0Flags); } else { Debug.LogLine("[ModifyFoxfs] foxfs.dat is already modified", Debug.LogLevel.Debug); } } else { MessageBox.Show(string.Format("Setup cancelled: SnakeBite failed to extract foxfs from chunk0."), "foxfs check failed", MessageBoxButtons.OK, MessageBoxIcon.Error); Debug.LogLine("[ModifyFoxfs] Process failed: could not check foxfs.dat", Debug.LogLevel.Debug); CleanupFolders(); return false; } Debug.LogLine("[ModifyFoxfs] Archive modification complete.", Debug.LogLevel.Debug); CleanupFolders(); return true; } catch (Exception e) { MessageBox.Show(string.Format("An error has occured while modifying foxfs in chunk0: {0}", e), "Exception Occurred", MessageBoxButtons.OK, MessageBoxIcon.Error); Debug.LogLine(string.Format("[ModifyFoxfs] Exception Occurred: {0}", e), Debug.LogLevel.Basic); Debug.LogLine("[ModifyFoxfs] SnakeBite has failed to modify foxfs in chunk0", Debug.LogLevel.Basic); return false; } }