//Cull any invalid entries that might have slipped in via older versions of snakebite public static void ValidateGameData(ref GameData gameData, ref List<string> zeroFiles) { Debug.LogLine("[ValidateGameData] Validating gameData files", Debug.LogLevel.Basic); Debug.LogLine("[ValidateGameData] Validating qar entries", Debug.LogLevel.Basic); for (int i = gameData.GameQarEntries.Count-1; i >= 0; i--) { ModQarEntry qarEntry = gameData.GameQarEntries[i]; if (!GzsLib.IsExtensionValidForArchive(qarEntry.FilePath, ".dat")) { Debug.LogLine($"[ValidateGameData] Found invalid file entry {qarEntry.FilePath} for archive {qarEntry.SourceName}", Debug.LogLevel.Basic); gameData.GameQarEntries.RemoveAt(i); zeroFiles.Remove(qarEntry.FilePath);//DEBUGNOW VERIFY } } Debug.LogLine("[ValidateGameData] Validating fpk entries", Debug.LogLevel.Basic); for (int i = gameData.GameFpkEntries.Count-1; i >= 0; i--) { ModFpkEntry fpkEntry = gameData.GameFpkEntries[i]; if (!GzsLib.IsExtensionValidForArchive(fpkEntry.FilePath, fpkEntry.FpkFile)) { Debug.LogLine($"[ValidateGameData] Found invalid file entry {fpkEntry.FilePath} for archive {fpkEntry.FpkFile}", Debug.LogLevel.Basic); gameData.GameFpkEntries.RemoveAt(i); } } }
/// <summary> /// /// </summary> /// <param name="sourceFpkFolder">path to folder that contains the files that will go into the fpk (with fpk-internal layout)</param> /// <param name="destFpkName"></param> /// <param name="rootDir">The root the fpk will go into, used by smakebite to derive the qar-internal path</param> /// <param name="fpkReferences"></param> /// <returns></returns> public static List <ModFpkEntry> BuildFpk(string sourceFpkFolder, string destFpkName, string rootDir, List <string> fpkReferences) { SnakeBite.Debug.LogLine($"[BuildFpk] {sourceFpkFolder}."); List <string> fpkFiles = new List <string>(); List <ModFpkEntry> fpkList = new List <ModFpkEntry>(); foreach (string fileName in Directory.GetFiles(sourceFpkFolder, "*.*", SearchOption.AllDirectories)) { if (!GzsLib.IsExtensionValidForArchive(fileName, destFpkName)) { SnakeBite.Debug.LogLine($"[BuildFpk] {fileName} is not a valid file for a {Path.GetExtension(destFpkName)} archive."); continue; } string inQarName = fileName.Substring(sourceFpkFolder.Length).Replace("\\", "/");//tex actually fpk-internal name, but as fpks are subsets of qars then? fpkFiles.Add(inQarName); fpkList.Add(new ModFpkEntry() { FilePath = inQarName, FpkFile = Tools.ToQarPath(destFpkName.Substring(rootDir.Length)),//qar-internal name of fpk ContentHash = Tools.GetMd5Hash(fileName) }); }//foreach filename GzsLib.WriteFpkArchive(destFpkName, sourceFpkFolder, fpkFiles, fpkReferences); return(fpkList); }//BuildFpk
public static List <ModFpkEntry> BuildFpk(string FpkFolder, string rootDir) { string FpkName = FpkFolder.Substring(FpkFolder.LastIndexOf("\\") + 1).Replace("_fpk", ".fpk"); string FpkBuildFolder = FpkFolder.Substring(0, FpkFolder.TrimEnd('\\').LastIndexOf("\\")); //string FpkXmlFile = FpkBuildFolder + "\\" + FpkName + ".xml"; string FpkFile = FpkBuildFolder + "\\" + FpkName; string FpkType = FpkFolder.Substring(FpkFolder.LastIndexOf("_") + 1); List <string> fpkFiles = new List <string>(); List <ModFpkEntry> fpkList = new List <ModFpkEntry>(); foreach (string FileName in Directory.GetFiles(FpkFolder, "*.*", SearchOption.AllDirectories)) { string xmlFileName = FileName.Substring(FpkFolder.Length).Replace("\\", "/"); fpkList.Add(new ModFpkEntry() { FilePath = xmlFileName, FpkFile = Tools.ToQarPath(FpkFile.Substring(rootDir.Length)), ContentHash = Tools.GetMd5Hash(FileName) }); fpkFiles.Add(xmlFileName); } GzsLib.WriteFpkArchive(FpkFile, FpkFolder, fpkFiles); return(fpkList); }
/// <summary> /// Merges the new mod files with the existing modded files and vanilla game files. /// The resulting file structure is the new _working0 folder to pack as 00.dat /// </summary> private static void InstallMods(List <string> modFilePaths, SettingsManager manager, List <string> pullFromVanillas, List <string> pullFromMods, ref HashSet <string> zeroFiles, ref List <string> oneFilesList, Dictionary <string, bool> pathUpdatesExist) { //Assumption: modded packs have already been extracted to _working0 directory - qarEntryEditList //Assumption: vanilla packs have already been extracted to _gameFpk directory (during AddToSettings) - fpkEntryRetrievalList //theoretically there should be no qar overlap between the _gamefpk(vanilla) and _working0(modded) files FastZip unzipper = new FastZip(); GameData gameData = manager.GetGameData(); foreach (string modFilePath in modFilePaths) { Debug.LogLine($"[Install] Installation started: {Path.GetFileName(modFilePath)}", Debug.LogLevel.Basic); Debug.LogLine($"[Install] Unzipping mod .mgsv ({Tools.GetFileSizeKB(modFilePath)} KB)", Debug.LogLevel.Basic); unzipper.ExtractZip(modFilePath, "_extr", "(.*?)"); Debug.LogLine("[Install] Load mod metadata", Debug.LogLevel.Basic); ModEntry extractedModEntry = new ModEntry("_extr\\metadata.xml"); if (pathUpdatesExist[extractedModEntry.Name]) { Debug.LogLine(string.Format("[Install] Checking for Qar path updates: {0}", extractedModEntry.Name), Debug.LogLevel.Basic); foreach (ModQarEntry modQar in extractedModEntry.ModQarEntries.Where(entry => !entry.FilePath.StartsWith("/Assets/"))) { string unhashedName = HashingExtended.UpdateName(modQar.FilePath); if (unhashedName != null) { Debug.LogLine(string.Format("[Install] Update successful: {0} -> {1}", modQar.FilePath, unhashedName), Debug.LogLevel.Basic); string workingOldPath = Path.Combine("_extr", Tools.ToWinPath(modQar.FilePath)); string workingNewPath = Path.Combine("_extr", Tools.ToWinPath(unhashedName)); if (!Directory.Exists(Path.GetDirectoryName(workingNewPath))) { Directory.CreateDirectory(Path.GetDirectoryName(workingNewPath)); } if (!File.Exists(workingNewPath)) { File.Move(workingOldPath, workingNewPath); } modQar.FilePath = unhashedName; } } } GzsLib.LoadModDictionary(extractedModEntry); ValidateModEntries(ref extractedModEntry); Debug.LogLine("[Install] Check mod FPKs against game .dat fpks", Debug.LogLevel.Basic); zeroFiles.UnionWith(MergePacks(extractedModEntry, pullFromVanillas, pullFromMods)); //foreach (string zeroFile in zeroFiles) Debug.LogLine(string.Format("Contained in zeroFiles: {0}", zeroFile)); Debug.LogLine("[Install] Copying loose textures to 01.", Debug.LogLevel.Basic); InstallLooseFtexs(extractedModEntry, ref oneFilesList); Debug.LogLine("[Install] Copying game dir files", Debug.LogLevel.Basic); InstallGameDirFiles(extractedModEntry, ref gameData); } manager.SetGameData(gameData); }
public static void PromoteBuildFiles(params string[] paths) { // Promote SB builds Debug.LogLine("[SB_Build] Promoting SB_Build files", Debug.LogLevel.Basic); foreach (string path in paths) { GzsLib.PromoteQarArchive(path + build_ext, path); } new SettingsManager(SnakeBiteSettings).UpdateDatHash(); }
/// <summary> /// move vanilla 00 files to 01, moves vanilla 01 textures to texture7, cleans snakebite.xml /// as DoWorkEventHandler /// </summary> public static void backgroundWorker_MergeAndCleanup(object sender, DoWorkEventArgs e) { BackgroundWorker mergeProcessor = (BackgroundWorker)sender; try { GzsLib.LoadDictionaries(); ClearBuildFiles(c7Path, t7Path, ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); ClearSBGameDir(); CleanupFolders(); mergeProcessor.ReportProgress(0, $"Migrating files to new archives ({Tools.GetFileSizeKB(ZeroPath, OnePath)} KB)"); if (!MoveDatFiles()) //moves vanilla 00 files into 01, excluding foxpatch. { Debug.LogLine("[DatMerge] Failed to complete archive migration. Cancelling..."); e.Cancel = true; ClearBuildFiles(c7Path, t7Path, ZeroPath, OnePath); return; } mergeProcessor.ReportProgress(0, $"Modfying foxfs in chunk0.dat ({Tools.GetFileSizeKB(chunk0Path)} KB)"); if (!ModifyFoxfs()) // adds lines to foxfs in chunk0. { Debug.LogLine("[ModifyFoxfs] Failed to complete Foxfs modification. Cancelling..."); e.Cancel = true; ClearBuildFiles(c7Path, t7Path, ZeroPath, OnePath, chunk0Path); return; } mergeProcessor.ReportProgress(0, "Promoting new archives"); PromoteBuildFiles(c7Path, t7Path, ZeroPath, OnePath, chunk0Path); // overwrites existing archives with modified archives mergeProcessor.ReportProgress(0, "Cleaning database"); CleanupDatabase(); } catch (Exception f) { MessageBox.Show(string.Format("An error has occurred while attempting to merge or clean up SnakeBite data: {0}", f), "Exception Occurred", MessageBoxButtons.OK, MessageBoxIcon.Error); Debug.LogLine(string.Format("[MergeAndCleanup] Exception Occurred: {0}", f), Debug.LogLevel.Basic); Debug.LogLine("[MergeAndCleanup] SnakeBite has failed to merge and clean up SnakeBite data", Debug.LogLevel.Basic); e.Cancel = true; try { ClearBuildFiles(c7Path, t7Path, ZeroPath, OnePath, chunk0Path); } catch (Exception g) { Debug.LogLine(string.Format("[MergeAndCleanup] Exception Occurred: {0}", g), Debug.LogLevel.Basic); Debug.LogLine("[MergeAndCleanup] SnakeBite has failed to remove the build files.", Debug.LogLevel.Basic); } return; } }
private static void MoveDatFilesClean(SettingsManager manager) { string sourceName = null; string destName = null; string destFolder = null; // lua files 00 -> 01, texture files 01 -> texture7, foxpatch 00 -> 00, chunkfiles 00 -> chunk7 Debug.LogLine("[DatMerge] First Time Setup Started", Debug.LogLevel.Debug); if (manager.SettingsExist()) manager.ClearAllMods(); List<string> zeroFiles = GzsLib.ExtractArchive<QarFile>(ZeroPath, "_extr"); List<string> chunk7Files = new List<string>(); List<string> oneFiles = new List<string>(); List<string> zeroOut = zeroFiles.ToList(); foreach (string zeroFile in zeroFiles) { if (zeroFile == "foxpatch.dat") continue; sourceName = Path.Combine("_extr", Tools.ToWinPath(zeroFile)); if (zeroFile.Contains(".lua")) { destName = Path.Combine("_working1", Tools.ToWinPath(zeroFile)); // 00 -> 01 oneFiles.Add(zeroFile); } else { destName = Path.Combine("_working2", Tools.ToWinPath(zeroFile)); // 00 -> chunk7 chunk7Files.Add(zeroFile); } zeroOut.Remove(zeroFile); destFolder = Path.GetDirectoryName(destName); if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); if (!File.Exists(destName)) File.Move(sourceName, destName); } // Build a_chunk7.dat.SB_Build GzsLib.WriteQarArchive(c7Path + build_ext, "_working2", chunk7Files, GzsLib.chunk7Flags); // Build a_texture7.dat.SB_Build File.Copy(OnePath, t7Path + build_ext, true); // Build 00.dat.SB_Build GzsLib.WriteQarArchive(ZeroPath + build_ext, "_extr", zeroOut, GzsLib.zeroFlags); // Build 01.dat.SB_Build GzsLib.WriteQarArchive(OnePath + build_ext, "_working1", oneFiles, GzsLib.oneFlags); }
public static void GenerateMgsv(string MgsvFile, string ModName, string SourceFolder) { ModEntry metaData = new ModEntry(); metaData.Name = ModName; metaData.Author = "SnakeBite"; metaData.MGSVersion.Version = "0.0.0.0"; metaData.SBVersion.Version = "0.8.0.0"; metaData.Version = "[QM]"; metaData.Description = "[Generated by SnakeBite]"; metaData.Website = ""; List <ModFpkEntry> fpkEntries = new List <ModFpkEntry>(); List <ModQarEntry> qarEntries = new List <ModQarEntry>(); foreach (var File in Directory.GetFiles(SourceFolder, "*", SearchOption.AllDirectories)) { string ShortFileName = File.Substring(SourceFolder.Length + 1); if (File.ToLower().Contains(".fpk")) { // do fpk var fpkCont = GzsLib.ListArchiveContents <FpkFile>(File); foreach (var fpkFile in fpkCont) { fpkEntries.Add(new ModFpkEntry() { FpkFile = ShortFileName, FilePath = fpkFile, SourceType = FileSource.Mod }); } } else { // do qar qarEntries.Add(new ModQarEntry() { FilePath = ShortFileName, SourceType = FileSource.Mod }); } } metaData.ModQarEntries = qarEntries; metaData.ModFpkEntries = fpkEntries; metaData.SaveToFile(Path.Combine(SourceFolder, "metadata.xml")); FastZip makeZip = new FastZip(); makeZip.CreateZip(MgsvFile, SourceFolder, true, ".*"); }
private void PopulateBoxes(string DataPath) { // unpack existing fpks foreach (string fpkFile in Directory.GetFiles(DataPath, "*.fpk*", SearchOption.AllDirectories)) { string fpkDir = Path.Combine(Path.GetDirectoryName(fpkFile), Path.GetFileName(fpkFile).Replace(".", "_")); if (!Directory.Exists(fpkDir)) { //extract fpk GzsLib.ExtractArchive <FpkFile>(fpkFile, fpkDir); } } foreach (string modFile in Directory.GetFiles(DataPath, "*.*", SearchOption.AllDirectories)) { string filePath = modFile.Substring(DataPath.Length).Replace("\\", "/"); if (Tools.IsValidFile(filePath) && filePath != "/metadata.xml") { listModFiles.Items.Add(filePath); } } if (File.Exists(Path.Combine(DataPath, "metadata.xml"))) { ModEntry modMetaData = new ModEntry(); modMetaData.ReadFromFile(Path.Combine(DataPath, "metadata.xml")); textModName.Text = modMetaData.Name; textModVersion.Text = modMetaData.Version; textModAuthor.Text = modMetaData.Author; textModWebsite.Text = modMetaData.Website; textModDescription.Text = modMetaData.Description.Replace("\n", "\r\n"); foreach (string li in comboForVersion.Items) { if (modMetaData.MGSVersion.AsString() == li) { comboForVersion.SelectedIndex = comboForVersion.Items.IndexOf(li); break; } } } if (File.Exists(DataPath + "\\readme.txt")) { StreamReader s = new StreamReader(DataPath + "\\readme.txt"); string readme = s.ReadToEnd(); textModDescription.Text = readme; } }
public static List <string> ListQarFiles(string PathName) { List <string> ListQarFolders = new List <string>(); List <string> ListQarFiles = new List <string>(); // Get a list of all folders to check for files (no _fpk/_fpkd) foreach (string Directory in Directory.GetDirectories(PathName, "*.*", SearchOption.AllDirectories)) { if (IsArchiveFolder(PathName, Directory)) { continue; } if (Directory.Substring(PathName.Length).Contains(ExternalDirName))// tex KLUDGE ignore MGS_TPP { continue; } ListQarFolders.Add(Directory); } ListQarFolders.Add(PathName); // Check all folders for files foreach (string Folder in ListQarFolders) { foreach (string FileName in Directory.GetFiles(Folder)) { string FilePath = FileName.Substring(Folder.Length); if (!GzsLib.IsExtensionValidForArchive(FileName, ".dat")) { Debug.LogLine($"[BuildArchive] {FileName} is not a valid file for a .dat archive."); continue; } if (!FilePath.Contains("metadata.xml") && !FilePath.Contains("readme.txt"))// ignore xml metadata and readme { ListQarFiles.Add(FileName); } } } return(ListQarFiles); }
private static void ValidateModEntries(ref ModEntry modEntry) { Debug.LogLine("[ValidateModEntries] Validating qar entries", Debug.LogLevel.Basic); for (int i = modEntry.ModQarEntries.Count - 1; i >= 0; i--) { ModQarEntry qarEntry = modEntry.ModQarEntries[i]; if (!GzsLib.IsExtensionValidForArchive(qarEntry.FilePath, ".dat")) { Debug.LogLine($"[ValidateModEntries] Found invalid file entry {qarEntry.FilePath} for archive {qarEntry.SourceName}", Debug.LogLevel.Basic); modEntry.ModQarEntries.RemoveAt(i); } } Debug.LogLine("[ValidateModEntries] Validating fpk entries", Debug.LogLevel.Basic); for (int i = modEntry.ModFpkEntries.Count - 1; i >= 0; i--) { ModFpkEntry fpkEntry = modEntry.ModFpkEntries[i]; if (!GzsLib.IsExtensionValidForArchive(fpkEntry.FilePath, fpkEntry.FpkFile)) { Debug.LogLine($"[ValidateModEntries] Found invalid file entry {fpkEntry.FilePath} for archive {fpkEntry.FpkFile}", Debug.LogLevel.Basic); modEntry.ModFpkEntries.RemoveAt(i); } } }
public static List <ModFpkEntry> BuildFpk(string FpkFolder, string rootDir) { Debug.LogLine($"[BuildFpk] {FpkFolder}."); string FpkName = FpkFolder.Substring(FpkFolder.LastIndexOf("\\") + 1).Replace("_fpk", ".fpk"); string FpkBuildFolder = FpkFolder.Substring(0, FpkFolder.TrimEnd('\\').LastIndexOf("\\")); //string FpkXmlFile = FpkBuildFolder + "\\" + FpkName + ".xml"; string FpkFile = FpkBuildFolder + "\\" + FpkName; string FpkType = FpkFolder.Substring(FpkFolder.LastIndexOf("_") + 1); List <string> fpkFiles = new List <string>(); List <ModFpkEntry> fpkList = new List <ModFpkEntry>(); foreach (string FileName in Directory.GetFiles(FpkFolder, "*.*", SearchOption.AllDirectories)) { if (!GzsLib.IsExtensionValidForArchive(FileName, FpkName)) { Debug.LogLine($"[BuildFpk] {FileName} is not a valid file for a {Path.GetExtension(FpkName)} archive."); continue; } string inQarName = FileName.Substring(FpkFolder.Length).Replace("\\", "/"); fpkList.Add(new ModFpkEntry() { FilePath = inQarName, FpkFile = Tools.ToQarPath(FpkFile.Substring(rootDir.Length)), ContentHash = Tools.GetMd5Hash(FileName) }); fpkFiles.Add(inQarName); } List <string> fpkFilesSorted = GzsLib.SortFpksFiles(FpkType, fpkFiles); GzsLib.WriteFpkArchive(FpkFile, FpkFolder, fpkFilesSorted); return(fpkList); }
public static bool InstallMods(List <string> ModFiles, bool skipCleanup = false) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Debug.LogLine("[Install] Start", Debug.LogLevel.Basic); ModManager.ClearBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); // deletes any leftover sb_build files that might still be in the directory (ie from a mid-process shutdown) ModManager.ClearSBGameDir(); // deletes the game directory sb_build ModManager.CleanupFolders(); // deletes the work folders which contain extracted files from 00/01 if (Properties.Settings.Default.AutosaveRevertPreset == true) { PresetManager.SavePreset(SavePresetPath + build_ext); // creates a backup preset file sb_build } else { Debug.LogLine("[Install] Skipping RevertChanges.MGSVPreset Save", Debug.LogLevel.Basic); } File.Copy(SnakeBiteSettings, SnakeBiteSettings + build_ext, true); // creates a settings sb_build GzsLib.LoadDictionaries(); List <ModEntry> installEntryList = new List <ModEntry>(); foreach (string modFile in ModFiles) { installEntryList.Add(Tools.ReadMetaData(modFile)); } List <string> zeroFiles = new List <string>(); bool hasQarZero = ModManager.hasQarZeroFiles(installEntryList); if (hasQarZero) { zeroFiles = GzsLib.ExtractArchive <QarFile>(ZeroPath, "_working0"); } List <string> oneFiles = null; bool hasFtexs = ModManager.foundLooseFtexs(installEntryList); if (hasFtexs) { oneFiles = GzsLib.ExtractArchive <QarFile>(OnePath, "_working1"); } SettingsManager SBBuildManager = new SettingsManager(SnakeBiteSettings + build_ext); var gameData = SBBuildManager.GetGameData(); ModManager.ValidateGameData(ref gameData, ref zeroFiles); var zeroFilesHashSet = new HashSet <string>(zeroFiles); Debug.LogLine("[Install] Building gameFiles lists", Debug.LogLevel.Basic); var baseGameFiles = GzsLib.ReadBaseData(); var allQarGameFiles = new List <Dictionary <ulong, GameFile> >(); allQarGameFiles.AddRange(baseGameFiles); try { ModManager.PrepGameDirFiles(); List <string> pullFromVanillas; List <string> pullFromMods; Dictionary <string, bool> pathUpdatesExist; Debug.LogLine("[Install] Writing FPK data to Settings", Debug.LogLevel.Basic); AddToSettingsFpk(installEntryList, SBBuildManager, allQarGameFiles, out pullFromVanillas, out pullFromMods, out pathUpdatesExist); InstallMods(ModFiles, SBBuildManager, pullFromVanillas, pullFromMods, ref zeroFilesHashSet, ref oneFiles, pathUpdatesExist); if (hasQarZero) { zeroFiles = zeroFilesHashSet.ToList(); zeroFiles.Sort(); GzsLib.WriteQarArchive(ZeroPath + build_ext, "_working0", zeroFiles, GzsLib.zeroFlags); } if (hasFtexs) { oneFiles.Sort(); GzsLib.WriteQarArchive(OnePath + build_ext, "_working1", oneFiles, GzsLib.oneFlags); } ModManager.PromoteGameDirFiles(); ModManager.PromoteBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); if (!skipCleanup) { ModManager.CleanupFolders(); ModManager.ClearSBGameDir(); } stopwatch.Stop(); Debug.LogLine($"[Install] Installation finished in {stopwatch.ElapsedMilliseconds} ms", Debug.LogLevel.Basic); return(true); } catch (Exception e) { stopwatch.Stop(); Debug.LogLine($"[Install] Installation failed at {stopwatch.ElapsedMilliseconds} ms", Debug.LogLevel.Basic); Debug.LogLine("[Install] Exception: " + e, Debug.LogLevel.Basic); MessageBox.Show("An error has occurred during the installation process and SnakeBite could not install the selected mod(s).\nException: " + e, "Mod(s) could not be installed", MessageBoxButtons.OK, MessageBoxIcon.Error); ModManager.ClearBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); ModManager.CleanupFolders(); bool restoreRetry = false; do { try { ModManager.RestoreBackupGameDir(SBBuildManager); } catch (Exception f) { Debug.LogLine("[Uninstall] Exception: " + f, Debug.LogLevel.Basic); restoreRetry = DialogResult.Retry == MessageBox.Show("SnakeBite could not restore Game Directory mod files due to the following exception: {f} \nWould you like to retry?", "Exception Occurred", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); } } while (restoreRetry); ModManager.ClearSBGameDir(); return(false); } }
public static bool InstallMod(string ModFile) { CleanupFolders(); Debug.LogLine(String.Format("[Mod] Installation started: {0}", ModFile), Debug.LogLevel.Basic); // Extract game archive var zeroFiles = GzsLib.ExtractArchive <QarFile>(ZeroPath, "_working"); // Extract mod data FastZip unzipper = new FastZip(); unzipper.ExtractZip(ModFile, "_extr", "(.*?)"); // Load mod metadata ModEntry metaData = new ModEntry("_extr\\metadata.xml"); // Build a list of FPKs contained in mod List <string> modFpks = new List <string>(); foreach (ModFpkEntry fpkEntry in metaData.ModFpkEntries) { if (!modFpks.Contains(fpkEntry.FpkFile)) { modFpks.Add(fpkEntry.FpkFile); } } List <string> mergeFpks = new List <string>(); List <ModQarEntry> MergeFiles = new List <ModQarEntry>(); Debug.LogLine("[Mod] Checking existing game data", Debug.LogLevel.Basic); // Check for FPKs in 00.dat foreach (string fpk in modFpks) { string datFile = zeroFiles.FirstOrDefault(file => Tools.CompareHashes(file, fpk)); if (datFile != null) { if (mergeFpks.Contains(Tools.ToQarPath(datFile))) { continue; } mergeFpks.Add(fpk); MergeFiles.Add(new ModQarEntry() { FilePath = fpk, SourceType = FileSource.Merged, SourceName = "00.dat" }); } } var gameData = GzsLib.ReadBaseData(); var oneFiles = gameData.FileList.FindAll(entry => entry.QarFile == "01.dat"); var baseFiles = gameData.FileList.FindAll(entry => entry.QarFile != "01.dat"); // Check for FPKs in 01.dat foreach (string fpk in modFpks) { GameFile file = oneFiles.FirstOrDefault(entry => entry.FileHash == Tools.NameToHash(fpk)); if (file != null) { if (mergeFpks.Contains(Tools.ToQarPath(file.FilePath))) { continue; } // Create destination directory string destDirectory = Path.Combine("_working", Path.GetDirectoryName(Tools.ToWinPath(file.FilePath))); if (!Directory.Exists(destDirectory)) { Directory.CreateDirectory(destDirectory); } // Extract file into dat directory var ex = GzsLib.ExtractFileByHash <QarFile>(Path.Combine(GameDir, "master\\0\\01.dat"), file.FileHash, Path.Combine("_working", Tools.ToWinPath(file.FilePath))); mergeFpks.Add(Tools.ToQarPath(file.FilePath)); MergeFiles.Add(new ModQarEntry() { FilePath = file.FilePath, SourceType = FileSource.Merged, SourceName = "01.dat" }); if (zeroFiles.FirstOrDefault(datFile => Tools.CompareHashes(datFile, file.FilePath)) == null) { zeroFiles.Add(Tools.ToWinPath(file.FilePath)); } } } // Check for FPKs in base data foreach (string fpk in modFpks) { GameFile file = baseFiles.FirstOrDefault(entry => entry.FileHash == Tools.NameToHash(fpk)); if (file != null) { if (mergeFpks.Contains(Tools.ToQarPath(file.FilePath))) { continue; } // Create destination directory string destDirectory = Path.Combine("_working", Path.GetDirectoryName(Tools.ToWinPath(file.FilePath))); if (!Directory.Exists(destDirectory)) { Directory.CreateDirectory(destDirectory); } // Extract file into dat directory var ex = GzsLib.ExtractFileByHash <QarFile>(Path.Combine(GameDir, "master\\" + file.QarFile), file.FileHash, Path.Combine("_working", Tools.ToWinPath(file.FilePath))); mergeFpks.Add(Tools.ToQarPath(file.FilePath)); MergeFiles.Add(new ModQarEntry() { FilePath = file.FilePath, SourceType = FileSource.Merged, SourceName = file.QarFile }); if (zeroFiles.FirstOrDefault(datFile => Tools.CompareHashes(datFile, file.FilePath)) == null) { zeroFiles.Add(Tools.ToWinPath(file.FilePath)); } } } Debug.LogLine(String.Format("[Mod] Merging {0} FPK files", MergeFiles.Count), Debug.LogLevel.Basic); var g = SettingsManager.GetGameData(); // Merge FPK files foreach (ModQarEntry gf in MergeFiles) { Debug.LogLine(String.Format("[Mod] Starting merge: {0} ({1})", gf.FilePath, gf.SourceName), Debug.LogLevel.Debug); // Extract game FPK string fpkDatPath = zeroFiles.FirstOrDefault(file => Tools.CompareHashes(file, gf.FilePath)); string fpkPath = Path.Combine("_working", Tools.ToWinPath(fpkDatPath)); var gameFpk = GzsLib.ExtractArchive <FpkFile>(fpkPath, "_gamefpk"); // Extract mod FPK var exFpk = GzsLib.ExtractArchive <FpkFile>(Path.Combine("_extr", Tools.ToWinPath(gf.FilePath)), "_modfpk"); // Add file to gamedata info var q = g.GameQarEntries.FirstOrDefault(entry => entry.FilePath == gf.FilePath); if (q == null) { g.GameQarEntries.Add(new ModQarEntry() { FilePath = Tools.ToQarPath(gf.FilePath), SourceType = gf.SourceType, SourceName = gf.SourceName, Hash = Tools.NameToHash(gf.FilePath) }); } foreach (string f in gameFpk) { var c = exFpk.FirstOrDefault(entry => Tools.CompareHashes(entry, f)); if (c == null) { if (g.GameFpkEntries.FirstOrDefault(entry => Tools.CompareHashes(entry.FpkFile, gf.FilePath) && Tools.CompareNames(entry.FilePath, f)) == null) { g.GameFpkEntries.Add(new ModFpkEntry() { FpkFile = Tools.ToQarPath(gf.FilePath), FilePath = f, SourceType = gf.SourceType, SourceName = gf.SourceName }); } } } // Merge contents foreach (string fileName in exFpk) { string fileDir = (Path.Combine("_gamefpk", Path.GetDirectoryName(fileName))); string sourceFile = Path.Combine("_modfpk", fileName); string destFile = Path.Combine("_gamefpk", fileName); Debug.LogLine(String.Format("[Mod] Copying file: {0}", fileName), Debug.LogLevel.All); if (!Directory.Exists(fileDir)) { Directory.CreateDirectory(fileDir); } File.Copy(sourceFile, destFile, true); if (!gameFpk.Contains(fileName)) { gameFpk.Add(Tools.ToQarPath(fileName)); } } // Rebuild game FPK GzsLib.WriteFpkArchive(fpkPath, "_gamefpk", gameFpk); if (!zeroFiles.Contains(Tools.ToWinPath(gf.FilePath))) { zeroFiles.Add(Tools.ToWinPath(gf.FilePath)); } Directory.Delete("_modfpk", true); Directory.Delete("_gamefpk", true); Debug.LogLine(String.Format("[Mod] Merge complete"), Debug.LogLevel.Debug); } SettingsManager.SetGameData(g); Debug.LogLine("[Mod] Copying remaining mod files", Debug.LogLevel.Basic); // Copy files for 01.dat, ignoring merged FPKs foreach (ModQarEntry modEntry in metaData.ModQarEntries) { if (!zeroFiles.Contains(Tools.ToWinPath(modEntry.FilePath))) { zeroFiles.Add(Tools.ToWinPath(modEntry.FilePath)); } if (modEntry.FilePath.Contains(".fpk")) { if (mergeFpks.Count(fpk => Tools.CompareHashes(fpk, modEntry.FilePath)) > 0) { continue; } } string sourceFile = Path.Combine("_extr", Tools.ToWinPath(modEntry.FilePath)); string destFile = Path.Combine("_working", Tools.ToWinPath(modEntry.FilePath)); string destDir = Path.GetDirectoryName(destFile); Debug.LogLine(String.Format("[Mod] Copying file: {0}", modEntry.FilePath), Debug.LogLevel.All); if (!Directory.Exists(destDir)) { Directory.CreateDirectory(destDir); } File.Copy(sourceFile, destFile, true); } // Rebuild 01.dat Debug.LogLine("[Mod] Rebuilding game archive", Debug.LogLevel.Basic); GzsLib.WriteQarArchive(ZeroPath, "_working", zeroFiles, 3150048); SettingsManager.UpdateDatHash(); SettingsManager.AddMod(metaData); Debug.LogLine("[Mod] Running database cleanup", Debug.LogLevel.Debug); CleanupDatabase(); CleanupFolders(); Debug.LogLine("[Mod] Installation finished", Debug.LogLevel.Basic); return(true); }
private void PopulateBoxes(string modPath) { // unpack existing fpks if extracted folders don't exist foreach (string fpkFile in Directory.GetFiles(modPath, "*.fpk*", SearchOption.AllDirectories)) { //tex chunk0\Assets\tpp\pack\collectible\common\col_common_tpp_fpk\Assets\tpp\pack\resident\resident00.fpkl is the only fpkl, don't know what a fpkl is, but gzcore crashes on it. also checks for xml in case user opened the fpk with gzstool and produced a xml file if (fpkFile.EndsWith(".fpkl") || fpkFile.EndsWith(".xml")) { continue; } string fpkDir = Path.Combine(Path.GetDirectoryName(fpkFile), Path.GetFileName(fpkFile).Replace(".", "_")); if (!Directory.Exists(fpkDir)) { //extract fpk GzsLib.ExtractArchive <FpkFile>(fpkFile, fpkDir); } } foreach (string modFile in Directory.GetFiles(modPath, "*.*", SearchOption.AllDirectories)) { string filePath = modFile.Substring(modPath.Length).Replace("\\", "/"); //GOTCHA: IsValidFile is only roughly accurate for this purpose, but listModFiles is only currently being used as non interactive user feedback so no big issue. if ((Tools.IsValidFile(filePath) || filePath.Contains("/GameDir")) && filePath != "/metadata.xml") { listModFiles.Items.Add(filePath); } } if (File.Exists(Path.Combine(modPath, "metadata.xml"))) { ModEntry modMetaData = new ModEntry(); modMetaData.ReadFromFile(Path.Combine(modPath, "metadata.xml")); textModName.Text = modMetaData.Name; textModVersion.Text = modMetaData.Version; textModAuthor.Text = modMetaData.Author; textModWebsite.Text = modMetaData.Website; textModDescription.Text = modMetaData.Description.Replace("\n", "\r\n"); string mgsvVersion = modMetaData.MGSVersion.AsString(); bool foundVersion = false; foreach (string li in comboForVersion.Items) { if (mgsvVersion == li) { comboForVersion.SelectedIndex = comboForVersion.Items.IndexOf(li); foundVersion = true; break; } } if (!foundVersion) { comboForVersion.Items.Add(mgsvVersion); comboForVersion.Text = mgsvVersion; } } if (File.Exists(modPath + "\\readme.txt")) { StreamReader s = new StreamReader(modPath + "\\readme.txt"); string readme = s.ReadToEnd(); textModDescription.Text = readme; } }
public static void BuildArchive(string SourceDir, ModEntry metaData, string outputFilePath) { Debug.LogLine($"[BuildArchive] {SourceDir}."); HashingExtended.ReadDictionary(); string buildDir = Directory.GetCurrentDirectory() + "\\_build"; try { if (Directory.Exists(buildDir)) { Directory.Delete(buildDir, true); } } catch { Debug.LogLine(string.Format("[BuildArchive] preexisting _build directory could not be deleted: {0}", buildDir)); } Directory.CreateDirectory("_build"); List <string> fpkFiles = Directory.GetFiles(SourceDir, "*.fpk*", SearchOption.AllDirectories).ToList(); for (int i = fpkFiles.Count - 1; i >= 0; i--) { string fpkFile = fpkFiles[i].Substring(SourceDir.Length + 1); if (!fpkFile.StartsWith("Assets")) { string updatedFileName = HashingExtended.UpdateName(fpkFile); if (updatedFileName != null) { updatedFileName = SourceDir + updatedFileName.Replace('/', '\\'); if (fpkFiles.Contains(updatedFileName)) { fpkFiles.Remove(fpkFiles[i]); } } } } List <string> fpkFolders = ListFpkFolders(SourceDir); for (int i = fpkFolders.Count - 1; i >= 0; i--) { string fpkFolder = fpkFolders[i].Substring(SourceDir.Length + 1); if (!fpkFolder.StartsWith("Assets")) { string updatedFileName = HashingExtended.UpdateName(fpkFolder.Replace("_fpk", ".fpk")); if (updatedFileName != null) { updatedFileName = SourceDir + updatedFileName.Replace('/', '\\'); if (fpkFolders.Contains(updatedFileName.Replace(".fpk", "_fpk")) || fpkFiles.Contains(updatedFileName)) { MessageBox.Show(string.Format("{0} was not packed or added to the build, because {1} (the unhashed filename of {0}) already exists in the mod directory.", Path.GetFileName(fpkFolders[i]), Path.GetFileName(updatedFileName))); fpkFolders.Remove(fpkFolders[i]); } } } } // check for FPKs that must be built and build metaData.ModFpkEntries = new List <ModFpkEntry>(); List <string> builtFpks = new List <string>(); foreach (string FpkFullDir in fpkFolders) { foreach (ModFpkEntry fpkEntry in BuildFpk(FpkFullDir, SourceDir)) { metaData.ModFpkEntries.Add(fpkEntry); if (!builtFpks.Contains(fpkEntry.FpkFile)) { builtFpks.Add(fpkEntry.FpkFile); } } } // check for other FPKs and build fpkentry data foreach (string SourceFile in Directory.GetFiles(SourceDir, "*.fpk*", SearchOption.AllDirectories)) { //tex chunk0\Assets\tpp\pack\collectible\common\col_common_tpp_fpk\Assets\tpp\pack\resident\resident00.fpkl is the only fpkl, don't know what a fpkl is, but gzcore crashes on it. if (SourceFile.EndsWith(".fpkl") || SourceFile.EndsWith(".xml")) { continue; } string FileName = Tools.ToQarPath(SourceFile.Substring(SourceDir.Length)); if (!builtFpks.Contains(FileName)) { // unpack FPK and build FPK list string fpkDir = Tools.ToWinPath(FileName.Replace(".fpk", "_fpk")); string fpkFullDir = Path.Combine(SourceDir, fpkDir); if (!Directory.Exists(fpkFullDir)) { GzsLib.ExtractArchive <FpkFile>(SourceFile, fpkFullDir); } var fpkContents = GzsLib.ListArchiveContents <FpkFile>(SourceFile); foreach (string file in fpkContents) { if (!GzsLib.IsExtensionValidForArchive(file, fpkDir)) { Debug.LogLine($"[BuildArchive] {file} is not a valid file for a {Path.GetExtension(fpkDir)} archive."); continue; } metaData.ModFpkEntries.Add(new ModFpkEntry() { FilePath = file, FpkFile = FileName, ContentHash = Tools.GetMd5Hash(Path.Combine(SourceDir, fpkDir, Tools.ToWinPath(file))) }); } } } // build QAR entries List <string> qarFiles = ListQarFiles(SourceDir); for (int i = qarFiles.Count - 1; i >= 0; i--) { string qarFile = qarFiles[i].Substring(SourceDir.Length + 1); if (!qarFile.StartsWith("Assets")) { string updatedQarName = HashingExtended.UpdateName(qarFile); if (updatedQarName != null) { updatedQarName = SourceDir + updatedQarName.Replace('/', '\\'); if (qarFiles.Contains(updatedQarName)) { MessageBox.Show(string.Format("{0} was not added to the build, because {1} (the unhashed filename of {0}) already exists in the mod directory.", Path.GetFileName(qarFiles[i]), Path.GetFileName(updatedQarName))); qarFiles.Remove(qarFiles[i]); } } } } metaData.ModQarEntries = new List <ModQarEntry>(); foreach (string qarFile in qarFiles) { string subDir = qarFile.Substring(0, qarFile.LastIndexOf("\\")).Substring(SourceDir.Length).TrimStart('\\'); // the subdirectory for XML output string qarFilePath = Tools.ToQarPath(qarFile.Substring(SourceDir.Length)); if (!Directory.Exists(Path.Combine("_build", subDir))) { Directory.CreateDirectory(Path.Combine("_build", subDir)); // create file structure } File.Copy(qarFile, Path.Combine("_build", Tools.ToWinPath(qarFilePath)), true); ulong hash = Tools.NameToHash(qarFilePath); metaData.ModQarEntries.Add(new ModQarEntry() { FilePath = qarFilePath, Compressed = qarFile.EndsWith(".fpk") || qarFile.EndsWith(".fpkd") ? true : false, ContentHash = Tools.GetMd5Hash(qarFile), Hash = hash }); } //tex build external entries metaData.ModFileEntries = new List <ModFileEntry>(); var externalFiles = ListExternalFiles(SourceDir); foreach (string externalFile in externalFiles) { string subDir = externalFile.Substring(0, externalFile.LastIndexOf("\\")).Substring(SourceDir.Length).TrimStart('\\'); // the subdirectory for XML output string externalFilePath = Tools.ToQarPath(externalFile.Substring(SourceDir.Length)); if (!Directory.Exists(Path.Combine("_build", subDir))) { Directory.CreateDirectory(Path.Combine("_build", subDir)); // create file structure } File.Copy(externalFile, Path.Combine("_build", Tools.ToWinPath(externalFilePath)), true); string strip = "/" + ExternalDirName; if (externalFilePath.StartsWith(strip)) { externalFilePath = externalFilePath.Substring(strip.Length); } //ulong hash = Tools.NameToHash(qarFilePath); metaData.ModFileEntries.Add(new ModFileEntry() { FilePath = externalFilePath, ContentHash = Tools.GetMd5Hash(externalFile) }); } metaData.SBVersion.Version = Application.ProductVersion; metaData.SaveToFile("_build\\metadata.xml"); // build archive FastZip zipper = new FastZip(); zipper.CreateZip(outputFilePath, "_build", true, "(.*?)"); try { Directory.Delete("_build", true); } catch (Exception e) { Debug.LogLine(string.Format("[BuildArchive] _build directory could not be deleted: {0}", e.ToString())); } }
public static bool UninstallMod(ModEntry mod) { Debug.LogLine(String.Format("[Mod] Uninstall started: {0}", mod.Name), Debug.LogLevel.Basic); CleanupFolders(); // Extract game archive var zeroFiles = GzsLib.ExtractArchive <QarFile>(ZeroPath, "_working"); // List all FPKs in mod List <string> modFpks = new List <string>(); foreach (ModFpkEntry fpkEntry in mod.ModFpkEntries) { if (!modFpks.Contains(fpkEntry.FpkFile)) { modFpks.Add(fpkEntry.FpkFile); } } var gameData = SettingsManager.GetGameData(); // Extract FPK foreach (string fpk in modFpks) { string fpkName = Path.GetFileName(fpk); string fpkDatPath = zeroFiles.FirstOrDefault(file => Tools.CompareHashes(file, fpk)); if (fpkDatPath == null) { continue; } var fpkFile = GzsLib.ExtractArchive <FpkFile>(Path.Combine("_working", Tools.ToWinPath(fpkDatPath)), "_modfpk"); // Remove all mod fpk files from fpk foreach (ModFpkEntry fpkEntry in mod.ModFpkEntries) { Debug.LogLine(String.Format("[Mod] Removing {1}\\{0}", Tools.ToWinPath(fpkEntry.FilePath), fpkName), Debug.LogLevel.Debug); fpkFile.RemoveAll(file => Tools.ToQarPath(file) == Tools.ToQarPath(fpkEntry.FilePath)); } var gameFpks = gameData.GameFpkEntries.ToList(); // remove all merged files from fpk foreach (ModFpkEntry gameFpkFile in gameFpks) { if (Tools.ToQarPath(gameFpkFile.FpkFile) == Tools.ToQarPath(fpk) && gameFpkFile.SourceType == FileSource.Merged) { Debug.LogLine(String.Format("[Mod] Removing merged file {0}", gameFpkFile.FilePath)); fpkFile.RemoveAll(entry => entry == gameFpkFile.FilePath); gameData.GameFpkEntries.Remove(gameFpkFile); } } // remove fpk if no files left if (fpkFile.Count == 0) { Debug.LogLine(String.Format("[Mod] {0} is empty, removing", fpkName), Debug.LogLevel.Debug); zeroFiles.RemoveAll(file => Tools.CompareHashes(file, fpk)); gameData.GameQarEntries.RemoveAll(file => Tools.CompareHashes(file.FilePath, fpk)); } else { Debug.LogLine(String.Format("[Mod] Rebuilding {0}", fpk), Debug.LogLevel.Debug); // rebuild fpk from base file var baseData = GzsLib.ReadBaseData(); var oneFiles = baseData.FileList.FindAll(entry => entry.QarFile == "01.dat"); var baseFiles = baseData.FileList.FindAll(entry => entry.QarFile != "01.dat"); // Check for FPKs in 00.dat first GameFile file = oneFiles.FirstOrDefault(entry => entry.FileHash == Tools.NameToHash(fpk)); if (file != null) { // Extract base FPK files GzsLib.ExtractFileByHash <QarFile>(Path.Combine(GameDir, "master\\0\\01.dat"), file.FileHash, "_working\\temp.fpk"); var gameFpk = GzsLib.ExtractArchive <FpkFile>("_working\\temp.fpk", "_gamefpk"); // Add merged base files to game file database var mCount = 0; foreach (var fpkF in gameFpk) { if (!fpkFile.Contains(fpkF)) { gameData.GameFpkEntries.Add(new ModFpkEntry() { FpkFile = fpk, FilePath = fpkF, SourceType = FileSource.Merged, SourceName = file.QarFile }); mCount++; } } Debug.LogLine(String.Format("[Mod] {0} files restored from {1}", mCount, file.QarFile), Debug.LogLevel.Debug); // Copy remaining files over base FPK foreach (string mFile in fpkFile) { string fDir = Path.GetDirectoryName(mFile); if (!Directory.Exists(Path.Combine("_gamefpk", fDir))) { Directory.CreateDirectory(Path.Combine("_gamefpk", fDir)); } Debug.LogLine(String.Format("[Mod] Merging existing file: {0}", mFile)); File.Copy(Path.Combine("_modfpk", mFile), Path.Combine(Path.Combine("_gamefpk", mFile)), true); if (!gameFpk.Contains(mFile)) { gameFpk.Add(mFile); } } // Rebuild FPK GzsLib.WriteFpkArchive(Path.Combine("_working", Tools.ToWinPath(fpkDatPath)), "_gamefpk", gameFpk); Directory.Delete("_gamefpk", true); Directory.Delete("_modfpk", true); continue; // don't check base data if it's in 01 } // check base files for FPK file = baseFiles.FirstOrDefault(entry => entry.FileHash == Tools.NameToHash(fpk)); if (file != null) { // Extract base FPK files GzsLib.ExtractFileByHash <QarFile>(Path.Combine(GameDir, "master\\" + file.QarFile), file.FileHash, "_working\\temp.fpk"); var gameFpk = GzsLib.ExtractArchive <FpkFile>("_working\\temp.fpk", "_gamefpk"); // Add merged base files to game file database var mCount = 0; foreach (var fpkF in gameFpk) { if (!fpkFile.Contains(fpkF)) { gameData.GameFpkEntries.Add(new ModFpkEntry() { FpkFile = fpk, FilePath = fpkF, SourceType = FileSource.Merged, SourceName = file.QarFile }); mCount++; } } Debug.LogLine(String.Format("[Mod] {0} files restored from {1}", mCount, file.QarFile), Debug.LogLevel.Debug); // Copy remaining files over base FPK foreach (string mFile in fpkFile) { string fDir = Path.GetDirectoryName(mFile); if (!Directory.Exists(Path.Combine("_gamefpk", fDir))) { Directory.CreateDirectory(Path.Combine("_gamefpk", fDir)); } Debug.LogLine(String.Format("[Mod] Merging existing file: {0}", mFile)); File.Copy(Path.Combine("_modfpk", mFile), Path.Combine(Path.Combine("_gamefpk", mFile)), true); if (!gameFpk.Contains(mFile)) { gameFpk.Add(mFile); } } // Rebuild FPK GzsLib.WriteFpkArchive(Path.Combine("_working", Tools.ToWinPath(fpk)), "_gamefpk", gameFpk); Directory.Delete("_gamefpk", true); Directory.Delete("_modfpk", true); } } } SettingsManager.SetGameData(gameData); // Remove all mod files from 01.dat foreach (ModQarEntry qarEntry in mod.ModQarEntries) { string fExt = Path.GetExtension(qarEntry.FilePath); if (!fExt.Contains(".fpk")) { zeroFiles.RemoveAll(file => Tools.CompareHashes(file, qarEntry.FilePath)); } } Debug.LogLine("[Mod] Rebuilding game archive", Debug.LogLevel.Basic); // Rebuild 01.dat GzsLib.WriteQarArchive(ZeroPath, "_working", zeroFiles, 3150048); SettingsManager.UpdateDatHash(); SettingsManager.RemoveMod(mod); CleanupDatabase(); CleanupFolders(); Debug.LogLine("[Mod] Uninstall complete", Debug.LogLevel.Basic); return(true); }
public static void BuildArchive(string SourceDir, ModEntry metaData, string outputFilePath) { string buildDir = Directory.GetCurrentDirectory() + "\\build"; if (Directory.Exists(buildDir)) { Directory.Delete(buildDir, true); } Directory.CreateDirectory("_build"); // check for FPKs that must be built and build metaData.ModFpkEntries = new List <ModFpkEntry>(); List <string> builtFpks = new List <string>(); foreach (string FpkDir in ListFpkFolders(SourceDir)) { foreach (ModFpkEntry fpkEntry in BuildFpk(FpkDir, SourceDir)) { metaData.ModFpkEntries.Add(fpkEntry); if (!builtFpks.Contains(fpkEntry.FpkFile)) { builtFpks.Add(fpkEntry.FpkFile); } } } // check for other FPKs and build fpkentry data foreach (string SourceFile in Directory.GetFiles(SourceDir, "*.fpk*", SearchOption.AllDirectories)) { //tex chunk0\Assets\tpp\pack\collectible\common\col_common_tpp_fpk\Assets\tpp\pack\resident\resident00.fpkl is the only fpkl, don't know what a fpkl is, but gzcore crashes on it. if (SourceFile.Contains(".fpkl")) { continue; } string FileName = Tools.ToQarPath(SourceFile.Substring(SourceDir.Length)); if (!builtFpks.Contains(FileName)) { // unpack FPK and build FPK list foreach (string file in GzsLib.ListArchiveContents <FpkFile>(SourceFile)) { string fpkDir = Tools.ToWinPath(FileName.Replace(".fpk", "_fpk")); string fpkFullDir = Path.Combine(SourceDir, fpkDir); if (!Directory.Exists(fpkFullDir)) { GzsLib.ExtractArchive <FpkFile>(SourceFile, fpkFullDir); } metaData.ModFpkEntries.Add(new ModFpkEntry() { FilePath = file, FpkFile = FileName, ContentHash = Tools.GetMd5Hash(Path.Combine(SourceDir, fpkDir, Tools.ToWinPath(file))) }); } } } // build QAR entries metaData.ModQarEntries = new List <ModQarEntry>(); foreach (string qarFile in ListQarFiles(SourceDir)) { string subDir = qarFile.Substring(0, qarFile.LastIndexOf("\\")).Substring(SourceDir.Length).TrimStart('\\'); // the subdirectory for XML output string qarFilePath = Tools.ToQarPath(qarFile.Substring(SourceDir.Length)); if (!Directory.Exists(Path.Combine("_build", subDir))) { Directory.CreateDirectory(Path.Combine("_build", subDir)); // create file structure } File.Copy(qarFile, Path.Combine("_build", Tools.ToWinPath(qarFilePath)), true); ulong hash = Tools.NameToHash(qarFilePath); metaData.ModQarEntries.Add(new ModQarEntry() { FilePath = qarFilePath, Compressed = qarFile.Substring(qarFile.LastIndexOf(".") + 1).Contains("fpk") ? true : false, ContentHash = Tools.GetMd5Hash(qarFile), Hash = hash }); } //tex build external entries metaData.ModFileEntries = new List <ModFileEntry>(); foreach (string externalFile in ListExternalFiles(SourceDir)) { string subDir = externalFile.Substring(0, externalFile.LastIndexOf("\\")).Substring(SourceDir.Length).TrimStart('\\'); // the subdirectory for XML output string externalFilePath = Tools.ToQarPath(externalFile.Substring(SourceDir.Length)); if (!Directory.Exists(Path.Combine("_build", subDir))) { Directory.CreateDirectory(Path.Combine("_build", subDir)); // create file structure } File.Copy(externalFile, Path.Combine("_build", Tools.ToWinPath(externalFilePath)), true); string strip = "/" + ExternalDirName; if (externalFilePath.StartsWith(strip)) { externalFilePath = externalFilePath.Substring(strip.Length); } //ulong hash = Tools.NameToHash(qarFilePath); metaData.ModFileEntries.Add(new ModFileEntry() { FilePath = externalFilePath, ContentHash = Tools.GetMd5Hash(externalFile) }); } metaData.SBVersion.Version = SnakeBiteVersionStr; metaData.SaveToFile("_build\\metadata.xml"); // build archive FastZip zipper = new FastZip(); zipper.CreateZip(outputFilePath, "_build", true, "(.*?)"); Directory.Delete("_build", true); }
public static void MoveDatFiles() { Debug.LogLine("[DatMerge] System data merge started", Debug.LogLevel.Debug); var zeroList = GzsLib.ListArchiveContents <QarFile>(ZeroPath); var modQarFiles = SettingsManager.GetModQarFiles(); int mc = 0; foreach (string zeroFile in zeroList) { if (zeroFile == "foxpatch.dat") { continue; } if (modQarFiles.Contains(Tools.ToQarPath(zeroFile))) { continue; } mc++; } if (mc > 0) { CleanupFolders(); // Extract 00.dat var zeroFiles = GzsLib.ExtractArchive <QarFile>(ZeroPath, "_extr"); // Extract 01.dat var oneFiles = GzsLib.ExtractArchive <QarFile>(OnePath, "_working"); var zeroOut = zeroFiles.ToList(); Debug.LogLine(string.Format("[DatMerge] Moving {0} system files", mc), Debug.LogLevel.Debug); // Move files from 00 to 01 (excluding foxpatch.dat) foreach (string zeroFile in zeroFiles) { if (zeroFile == "foxpatch.dat") { continue; } if (modQarFiles.Contains(Tools.ToQarPath(zeroFile))) { continue; } string sourceName = Path.Combine("_extr", Tools.ToWinPath(zeroFile)); string destName = Path.Combine("_working", Tools.ToWinPath(zeroFile)); string destFolder = Path.GetDirectoryName(destName); Debug.LogLine(string.Format("[DatMerge] Moving system file {0}", zeroFile)); if (!Directory.Exists(destFolder)) { Directory.CreateDirectory(destFolder); } File.Move(sourceName, destName); zeroOut.Remove(zeroFile); oneFiles.Add(zeroFile); } Debug.LogLine("[DatMerge] Rebuilding game archives", Debug.LogLevel.Debug); // Rebuild 01.dat with files GzsLib.WriteQarArchive(OnePath, "_working", oneFiles, 3150048); // Rebuild 00.dat GzsLib.WriteQarArchive(ZeroPath, "_extr", zeroOut, 3150304); SettingsManager.UpdateDatHash(); CleanupFolders(); Debug.LogLine("[DatMerge] Merge finished", Debug.LogLevel.Debug); } else { Debug.LogLine("[DatMerge] No files to merge, aborting", Debug.LogLevel.Debug); } }
public static bool UninstallMods(CheckedListBox.CheckedIndexCollection modIndices, bool skipCleanup = false) // Uninstalls mods based on their indices in the list { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Debug.LogLine("[Uninstall] Start", Debug.LogLevel.Basic); // initial cleanup ModManager.ClearBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); ModManager.ClearSBGameDir(); ModManager.CleanupFolders(); // backup preset build if (Properties.Settings.Default.AutosaveRevertPreset == true) { PresetManager.SavePreset(SavePresetPath + build_ext); } else { Debug.LogLine("[Uninstall] Skipping RevertChanges.MGSVPreset Save", Debug.LogLevel.Basic); } GzsLib.LoadDictionaries(); File.Copy(SnakeBiteSettings, SnakeBiteSettings + build_ext, true); List <ModEntry> mods = SBBuildManager.GetInstalledMods(); List <ModEntry> selectedMods = new List <ModEntry>(); foreach (int index in modIndices) { ModEntry mod = mods[index]; selectedMods.Add(mod); } List <string> zeroFiles = new List <string>(); bool hasQarZero = ModManager.hasQarZeroFiles(selectedMods); if (hasQarZero) { // if necessary, extracts 00.dat and creates a list of filenames, which is pruned throughout the uninstall process and repacked at the end. zeroFiles = GzsLib.ExtractArchive <QarFile>(ZeroPath, "_working0"); zeroFiles.RemoveAll(file => file.EndsWith("_unknown")); } List <string> oneFiles = null; bool hasFtexs = ModManager.foundLooseFtexs(selectedMods); if (hasFtexs) { // if necessary, extracts 01.dat and creates a list of filenames similar to zeroFiles. only textures are pruned from the list. oneFiles = GzsLib.ExtractArchive <QarFile>(OnePath, "_working1"); oneFiles.RemoveAll(file => file.EndsWith("_unknown")); } //end of qar extraction GameData gameData = SBBuildManager.GetGameData(); ModManager.ValidateGameData(ref gameData, ref zeroFiles); Debug.LogLine("[Uninstall] Building gameFiles lists", Debug.LogLevel.Basic); var baseGameFiles = GzsLib.ReadBaseData(); try { ModManager.PrepGameDirFiles(); // begin uninstall UninstallMods(selectedMods, ref zeroFiles, ref oneFiles); if (hasQarZero) { zeroFiles.Sort(); GzsLib.WriteQarArchive(ZeroPath + build_ext, "_working0", zeroFiles, GzsLib.zeroFlags); } if (hasFtexs) { oneFiles.Sort(); GzsLib.WriteQarArchive(OnePath + build_ext, "_working1", oneFiles, GzsLib.oneFlags); } // end of qar rebuild // overwrite old mod data ModManager.PromoteGameDirFiles(); ModManager.PromoteBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); if (!skipCleanup) { ModManager.CleanupFolders(); ModManager.ClearSBGameDir(); } Debug.LogLine("[Uninstall] Uninstall complete", Debug.LogLevel.Basic); stopwatch.Stop(); Debug.LogLine($"[Uninstall] Uninstall took {stopwatch.ElapsedMilliseconds} ms", Debug.LogLevel.Basic); return(true); } catch (Exception e) { Debug.LogLine("[Uninstall] Exception: " + e, Debug.LogLevel.Basic); stopwatch.Stop(); Debug.LogLine($"[Uninstall] Uninstall failed at {stopwatch.ElapsedMilliseconds} ms", Debug.LogLevel.Basic); MessageBox.Show("An error has occurred during the uninstallation process and SnakeBite could not uninstall the selected mod(s).\nException: " + e); // clean up failed files ModManager.ClearBuildFiles(ZeroPath, OnePath, SnakeBiteSettings, SavePresetPath); ModManager.CleanupFolders(); bool restoreRetry = false; do { try { ModManager.RestoreBackupGameDir(SBBuildManager); } catch (Exception f) { Debug.LogLine("[Uninstall] Exception: " + f, Debug.LogLevel.Basic); restoreRetry = DialogResult.Retry == MessageBox.Show("SnakeBite could not restore Game Directory mod files due to the following exception: {f} \nWould you like to retry?", "Exception Occurred", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); } } while (restoreRetry); ModManager.ClearSBGameDir(); return(false); } }//UninstallMod batch
private static HashSet <string> MergePacks(ModEntry extractedModEntry, List <string> pullFromVanillas, List <string> pullFromMods) { HashSet <string> modQarZeroPaths = new HashSet <string>(); foreach (ModQarEntry modQar in extractedModEntry.ModQarEntries) { string workingDestination = Path.Combine("_working0", Tools.ToWinPath(modQar.FilePath)); if (!Directory.Exists(Path.GetDirectoryName(workingDestination))) { Directory.CreateDirectory(Path.GetDirectoryName(workingDestination)); } string modQarSource = Path.Combine("_extr", Tools.ToWinPath(modQar.FilePath)); string existingQarSource; if (pullFromMods.FirstOrDefault(e => e == modQar.FilePath) != null) { //Debug.LogLine(string.Format("{0}'s Qar file '{1}' will pull from _working0 (modded)", extractedModEntry.Name, modQar.FilePath)); existingQarSource = workingDestination; } else { int indexToRemove = pullFromVanillas.FindIndex(m => m == modQar.FilePath); // remove from retrievalfilepaths and add to editlist if (indexToRemove >= 0) { //Debug.LogLine(string.Format("{0}'s Qar file '{1}' will pull from _gameFpk (fresh game file)", extractedModEntry.Name, modQar.FilePath)); existingQarSource = Path.Combine("_gameFpk", Tools.ToWinPath(modQar.FilePath)); pullFromVanillas.RemoveAt(indexToRemove); pullFromMods.Add(modQar.FilePath); } else { //Debug.LogLine(string.Format("{0}'s Qar file '{1}' is non-native or not mergeable", extractedModEntry.Name, modQar.FilePath)); existingQarSource = null; if (modQar.FilePath.EndsWith(".fpk") || modQar.FilePath.EndsWith(".fpkd")) { pullFromMods.Add(modQar.FilePath); // for merging non-native fpk files consecutively } } } if (existingQarSource != null) { var pulledPack = GzsLib.ExtractArchive <FpkFile>(existingQarSource, "_build"); var extrPack = GzsLib.ExtractArchive <FpkFile>(modQarSource, "_build"); pulledPack = pulledPack.Union(extrPack).ToList(); //foreach(string file in extrPack) Debug.LogLine(string.Format("{0} is listed in the archive extr", file)); //TODO: merge referenced from mod archive to (but then that would probably be tricky keeping track of for Unmerge) //I think only fpks have references (not fpkd), but whatev. var fpkReferences = GzsLib.GetFpkReferences(existingQarSource); GzsLib.WriteFpkArchive(workingDestination, "_build", pulledPack, fpkReferences); } else { File.Copy(modQarSource, workingDestination, true); } if (!modQar.FilePath.Contains(".ftex")) { //Debug.LogLine(string.Format("Adding {0}'s Qar file '{1}' to 00.dat", extractedModEntry.Name, modQar.FilePath)); modQarZeroPaths.Add(Tools.ToWinPath(modQar.FilePath)); } } return(modQarZeroPaths); }
/// <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); }
// 00 non-snakebite Files to 01, 01 lua files unchanged, 01 textures -> t7, 01 chunkfiles -> c7, private static void MoveDatFilesDirty(SettingsManager manager) { var modQarFiles = manager.GetModQarFiles(); string sourceName = null; string destName = null; string destFolder = null; Debug.LogLine("[DatMerge] Dispersing files from 00 to 01, and then from 01 to a_chunk7 and a_texture7 if necessary.", Debug.LogLevel.Debug); List<string> oneFiles = GzsLib.ExtractArchive<QarFile>(OnePath, "_extr"); List<string> zeroList = new List<string>(); int moveCount = 0; try { zeroList = GzsLib.ListArchiveContents<QarFile>(ZeroPath); } catch (Exception e) { Debug.LogLine(String.Format("[Error] GzsLib.ListArchiveContents exception: {0}", e.ToString()), Debug.LogLevel.Debug); throw e; } foreach (string zeroFile in zeroList) { if (zeroFile == "foxpatch.dat") continue; if (modQarFiles.Contains(Tools.ToQarPath(zeroFile)) || oneFiles.Contains(zeroFile)) continue; if (oneFiles.Contains(zeroFile)) continue; moveCount++; } if (moveCount > 0) //if any non-snakebite files exist in 00, move them to 01. { Debug.LogLine("[DatMerge] Moving files to 01.dat.", Debug.LogLevel.Debug); List<string> zeroFiles = GzsLib.ExtractArchive<QarFile>(ZeroPath, "_working1"); List<string> zeroOut = zeroFiles.ToList(); foreach (string zeroFile in zeroFiles) { if (zeroFile == "foxpatch.dat") continue; if (modQarFiles.Contains(Tools.ToQarPath(zeroFile))) continue; if (oneFiles.Contains(zeroFile)) { zeroOut.Remove(zeroFile); continue; } //if it already exists in 01 then there's nowhere for it to go. sourceName = Path.Combine("_working1", Tools.ToWinPath(zeroFile)); destName = Path.Combine("_extr", Tools.ToWinPath(zeroFile)); oneFiles.Add(zeroFile); // 00 -> 01 zeroOut.Remove(zeroFile); destFolder = Path.GetDirectoryName(destName); if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); if (!File.Exists(destName)) File.Move(sourceName, destName); } GzsLib.WriteQarArchive(ZeroPath + build_ext, "_working1", zeroOut, GzsLib.zeroFlags); // rebuild 00 archive Directory.Delete("_working1", true); // clean up _working1, to be used by texture7 while (Directory.Exists("_working1")) Thread.Sleep(100); } moveCount = 0; // check if any files need to be moved to C7/T7 int textureCount = 0; List<string> chunk7List = new List<string>(); List<string> tex7List = new List<string>(); try { if (File.Exists(t7Path)) tex7List = GzsLib.ListArchiveContents<QarFile>(t7Path); if (File.Exists(c7Path)) chunk7List = GzsLib.ListArchiveContents<QarFile>(c7Path); } catch (Exception e) { Debug.LogLine(String.Format("[Error] GzsLib.ListArchiveContents exception: {0}", e.ToString()), Debug.LogLevel.Debug); throw e; } foreach (string oneFile in oneFiles) { if (modQarFiles.Contains(Tools.ToQarPath(oneFile)) || tex7List.Contains(oneFile) || chunk7List.Contains(oneFile)) continue; if (oneFile.Contains(".lua")) continue; // vanilla lua files must stay in 01 if (oneFile.Contains(".ftex")) textureCount++; moveCount++; } if (moveCount > 0) { List<string> oneOut = oneFiles.ToList(); if (textureCount > 0) // if non-snakebite textures exist, move them to t7 { Debug.LogLine("[DatMerge] Moving files to a_texture7.dat.", Debug.LogLevel.Debug); List<string> texture7Files = new List<string>(); if (File.Exists(t7Path)) texture7Files = GzsLib.ExtractArchive<QarFile>(t7Path, "_working1"); foreach (string oneFile in oneFiles) // once 00 files have been moved, move 01 files into t7, c7. { if (modQarFiles.Contains(Tools.ToQarPath(oneFile))) continue; if (oneFile.Contains(".ftex")) { sourceName = Path.Combine("_extr", Tools.ToWinPath(oneFile)); destName = Path.Combine("_working1", Tools.ToWinPath(oneFile)); // 01 -> texture7 destFolder = Path.GetDirectoryName(destName); if (!texture7Files.Contains(oneFile)) texture7Files.Add(oneFile); oneOut.Remove(oneFile); if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); if (!File.Exists(destName)) File.Move(sourceName, destName); } } GzsLib.WriteQarArchive(t7Path + build_ext, "_working1", texture7Files, GzsLib.texture7Flags); } oneFiles = oneOut.ToList(); // update oneFiles to remove any .ftex already found if (oneFiles.Count > 0) // if any other files need to be moved, they go in chunk7 { Debug.LogLine("[DatMerge] Moving files to a_chunk7.dat.", Debug.LogLevel.Debug); List<string> chunk7Files = new List<string>(); if (File.Exists(c7Path)) chunk7Files = GzsLib.ExtractArchive<QarFile>(c7Path, "_working2"); foreach (string oneFile in oneFiles) // once 00 files have been moved, move 01 files into t7, c7. { if (modQarFiles.Contains(Tools.ToQarPath(oneFile))) continue; if (oneFile.Contains(".lua")) continue; sourceName = Path.Combine("_extr", Tools.ToWinPath(oneFile)); destName = Path.Combine("_working2", Tools.ToWinPath(oneFile)); // 00 -> chunk7 destFolder = Path.GetDirectoryName(destName); if (!chunk7Files.Contains(oneFile)) chunk7Files.Add(oneFile); oneOut.Remove(oneFile); if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); if (!File.Exists(destName)) File.Move(sourceName, destName); } GzsLib.WriteQarArchive(c7Path + build_ext, "_working2", chunk7Files, GzsLib.chunk7Flags); // rebuild chunk7 archive } GzsLib.WriteQarArchive(OnePath + build_ext, "_extr", oneOut, GzsLib.oneFlags); // rebuild 01 archive } }
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; } }
}//InstallGameDirFiles private static void AddToSettingsFpk(List <ModEntry> installEntryList, SettingsManager manager, List <Dictionary <ulong, GameFile> > allQarGameFiles, out List <string> PullFromVanillas, out List <string> pullFromMods, out Dictionary <string, bool> pathUpdatesExist) { GameData gameData = manager.GetGameData(); pathUpdatesExist = new Dictionary <string, bool>(); List <string> newModQarEntries = new List <string>(); List <string> modQarFiles = manager.GetModQarFiles(); pullFromMods = new List <string>(); foreach (ModEntry modToInstall in installEntryList) { Dictionary <string, string> newNameDictionary = new Dictionary <string, string>(); int foundUpdate = 0; foreach (ModQarEntry modQar in modToInstall.ModQarEntries.Where(entry => !entry.FilePath.StartsWith("/Assets/"))) { //Debug.LogLine(string.Format("Attempting to update Qar filename: {0}", modQar.FilePath), Debug.LogLevel.Basic); string unhashedName = HashingExtended.UpdateName(modQar.FilePath); if (unhashedName != null) { //Debug.LogLine(string.Format("Success: {0}", unhashedName), Debug.LogLevel.Basic); newNameDictionary.Add(modQar.FilePath, unhashedName); foundUpdate++; modQar.FilePath = unhashedName; if (!pathUpdatesExist.ContainsKey(modToInstall.Name)) { pathUpdatesExist.Add(modToInstall.Name, true); } else { pathUpdatesExist[modToInstall.Name] = true; } } } if (foundUpdate > 0) { foreach (ModFpkEntry modFpkEntry in modToInstall.ModFpkEntries) { string unHashedName; if (newNameDictionary.TryGetValue(modFpkEntry.FpkFile, out unHashedName)) { modFpkEntry.FpkFile = unHashedName; } } } else if (!pathUpdatesExist.ContainsKey(modToInstall.Name)) { pathUpdatesExist.Add(modToInstall.Name, false); } manager.AddMod(modToInstall); //foreach (ModQarEntry modqar in modToInstall.ModQarEntries) Debug.LogLine("Mod Qar in modToInstall: " + modqar.FilePath); foreach (ModQarEntry modQarEntry in modToInstall.ModQarEntries) // add qar entries (fpk, fpkd) to GameData if they don't already exist { string modQarFilePath = modQarEntry.FilePath; if (!(modQarFilePath.EndsWith(".fpk") || modQarFilePath.EndsWith(".fpkd"))) { continue; // only pull for Qar's with Fpk's } if (modQarFiles.Any(entry => entry == modQarFilePath)) { pullFromMods.Add(modQarFilePath); //Debug.LogLine("Pulling from 00.dat: {0} " + modQarFilePath); } else if (!newModQarEntries.Contains(modQarFilePath)) { newModQarEntries.Add(modQarFilePath); //Debug.LogLine("Pulling from base archives: {0} " + modQarFilePath); } } } //Debug.LogLine(string.Format("Foreach nest 1 complete")); List <ModFpkEntry> newModFpkEntries = new List <ModFpkEntry>(); foreach (ModEntry modToInstall in installEntryList) { foreach (ModFpkEntry modFpkEntry in modToInstall.ModFpkEntries) { //Debug.LogLine(string.Format("Checking out {0} from {1}", modFpkEntry.FilePath, modFpkEntry.FpkFile)); if (newModQarEntries.Contains(modFpkEntry.FpkFile)) // it isn't already part of the snakebite environment { //Debug.LogLine(string.Format("seeking repair files around {0}", modFpkEntry.FilePath)); newModFpkEntries.Add(modFpkEntry); } else { //Debug.LogLine(string.Format("Removing {0} from gameFpkEntries so it will only be listed in the mod's entries", modFpkEntry.FilePath)); int indexToRemove = gameData.GameFpkEntries.FindIndex(m => m.FilePath == Tools.ToWinPath(modFpkEntry.FilePath)); // this will remove the gamedata's listing of the file under fpkentries (repair entries), so the filepath will only be listed in the modentry if (indexToRemove >= 0) { gameData.GameFpkEntries.RemoveAt(indexToRemove); } } } } //Debug.LogLine(string.Format("Foreach nest 2 complete")); HashSet <ulong> mergeFpkHashes = new HashSet <ulong>(); PullFromVanillas = new List <string>(); var repairFpkEntries = new List <ModFpkEntry>(); foreach (ModFpkEntry newFpkEntry in newModFpkEntries) // this will add the fpkentry listings (repair entries) to the settings xml { //Debug.LogLine(string.Format("checking {0} for repairs", newFpkEntry.FilePath)); ulong packHash = Tools.NameToHash(newFpkEntry.FpkFile); if (mergeFpkHashes.Contains(packHash)) { continue; // the process has already plucked this particular qar file } foreach (var archiveQarGameFiles in allQarGameFiles) // check every archive (except 00) to see if the particular qar file already exists { //Debug.LogLine(string.Format("checking archive for an existing qar file")); if (archiveQarGameFiles.Count > 0) { GameFile existingPack = null; archiveQarGameFiles.TryGetValue(packHash, out existingPack); if (existingPack != null) // the qar file is found { //Debug.LogLine(string.Format("Qar file {0} found in {1}. adding to gameqarentries", newFpkEntry.FpkFile, existingPack.QarFile)); mergeFpkHashes.Add(packHash); gameData.GameQarEntries.Add(new ModQarEntry { FilePath = newFpkEntry.FpkFile, SourceType = FileSource.Merged, SourceName = existingPack.QarFile, Hash = existingPack.FileHash }); PullFromVanillas.Add(newFpkEntry.FpkFile); string windowsFilePath = Tools.ToWinPath(newFpkEntry.FpkFile); // Extract the pack file from the vanilla game files, place into _gamefpk for future use string sourceArchive = Path.Combine(GameDir, "master\\" + existingPack.QarFile); string workingPath = Path.Combine("_gameFpk", windowsFilePath); if (!Directory.Exists(Path.GetDirectoryName(workingPath))) { Directory.CreateDirectory(Path.GetDirectoryName(workingPath)); } GzsLib.ExtractFileByHash <QarFile>(sourceArchive, existingPack.FileHash, workingPath); // extracts the specific .fpk from the game data foreach (string listedFile in GzsLib.ListArchiveContents <FpkFile>(workingPath)) { repairFpkEntries.Add(new ModFpkEntry { FpkFile = newFpkEntry.FpkFile, FilePath = listedFile, SourceType = FileSource.Merged, SourceName = existingPack.QarFile }); //Debug.LogLine(string.Format("File Listed: {0} in {1}", extractedFile, newFpkEntry.FpkFile)); } break; } } } } //Debug.LogLine(string.Format("Foreach nest 3 complete")); foreach (ModFpkEntry newFpkEntry in newModFpkEntries) // finally, strip away the modded entries from the repair entries { //Debug.LogLine(string.Format("checking to remove {0} from gamefpkentries", Tools.ToWinPath(newFpkEntry.FilePath))); int indexToRemove = repairFpkEntries.FindIndex(m => m.FilePath == Tools.ToWinPath(newFpkEntry.FilePath)); if (indexToRemove >= 0) { repairFpkEntries.RemoveAt(indexToRemove); } } gameData.GameFpkEntries = gameData.GameFpkEntries.Union(repairFpkEntries).ToList(); manager.SetGameData(gameData); }
public static void BuildArchive(string SourceDir, ModEntry metaData, string OutputFile) { string buildDir = Directory.GetCurrentDirectory() + "\\build"; if (Directory.Exists(buildDir)) { Directory.Delete(buildDir, true); } Directory.CreateDirectory("_build"); // check for FPKs that must be built and build metaData.ModFpkEntries = new List <ModFpkEntry>(); List <string> builtFpks = new List <string>(); foreach (string FpkDir in ListFpkFolders(SourceDir)) { foreach (ModFpkEntry fpkEntry in BuildFpk(FpkDir, SourceDir)) { metaData.ModFpkEntries.Add(fpkEntry); if (!builtFpks.Contains(fpkEntry.FpkFile)) { builtFpks.Add(fpkEntry.FpkFile); } } } // check for other FPKs and build fpkentry data foreach (string SourceFile in Directory.GetFiles(SourceDir, "*.fpk*", SearchOption.AllDirectories)) { string FileName = Tools.ToQarPath(SourceFile.Substring(SourceDir.Length)); if (!builtFpks.Contains(FileName)) { // unpack FPK and build FPK list foreach (string file in GzsLib.ListArchiveContents <FpkFile>(SourceFile)) { string fpkDir = Tools.ToWinPath(FileName.Replace(".fpk", "_fpk")); string fpkFullDir = Path.Combine(SourceDir, fpkDir); if (!Directory.Exists(fpkFullDir)) { GzsLib.ExtractArchive <FpkFile>(SourceFile, fpkFullDir); } metaData.ModFpkEntries.Add(new ModFpkEntry() { FilePath = file, FpkFile = FileName, ContentHash = Tools.GetMd5Hash(Path.Combine(SourceDir, fpkDir, Tools.ToWinPath(file))) }); } } } // build QAR entries metaData.ModQarEntries = new List <ModQarEntry>(); foreach (string qarFile in ListQarFiles(SourceDir)) { string subDir = qarFile.Substring(0, qarFile.LastIndexOf("\\")).Substring(SourceDir.Length).TrimStart('\\'); // the subdirectory for XML output string qarFilePath = Tools.ToQarPath(qarFile.Substring(SourceDir.Length)); if (!Directory.Exists(Path.Combine("_build", subDir))) { Directory.CreateDirectory(Path.Combine("_build", subDir)); // create file structure } File.Copy(qarFile, Path.Combine("_build", Tools.ToWinPath(qarFilePath)), true); ulong hash = Tools.NameToHash(qarFilePath); metaData.ModQarEntries.Add(new ModQarEntry() { FilePath = qarFilePath, Compressed = qarFile.Substring(qarFile.LastIndexOf(".") + 1).Contains("fpk") ? true : false, ContentHash = Tools.GetMd5Hash(qarFile), Hash = hash }); } metaData.SBVersion.Version = "0.8.0.0"; metaData.SaveToFile("_build\\metadata.xml"); // build archive FastZip zipper = new FastZip(); zipper.CreateZip(OutputFile, "_build", true, "(.*?)"); Directory.Delete("_build", true); }
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 List <ModFpkEntry> BuildFpk(string FpkFolder, string rootDir) { string FpkName = FpkFolder.Substring(FpkFolder.LastIndexOf("\\") + 1).Replace("_fpk", ".fpk"); string FpkBuildFolder = FpkFolder.Substring(0, FpkFolder.TrimEnd('\\').LastIndexOf("\\")); //string FpkXmlFile = FpkBuildFolder + "\\" + FpkName + ".xml"; string FpkFile = FpkBuildFolder + "\\" + FpkName; string FpkType = FpkFolder.Substring(FpkFolder.LastIndexOf("_") + 1); List <string> fpkFiles = new List <string>(); List <ModFpkEntry> fpkList = new List <ModFpkEntry>(); foreach (string FileName in Directory.GetFiles(FpkFolder, "*.*", SearchOption.AllDirectories)) { string xmlFileName = FileName.Substring(FpkFolder.Length).Replace("\\", "/"); fpkList.Add(new ModFpkEntry() { FilePath = xmlFileName, FpkFile = Tools.ToQarPath(FpkFile.Substring(rootDir.Length)), ContentHash = Tools.GetMd5Hash(FileName) }); fpkFiles.Add(xmlFileName); } List <string> fpkFilesSorted = fpkFiles.OrderBy(fileName => Path.GetExtension(fileName)).ThenBy(fileName => fileName).ToList(); //tex all positions reletively solid except for "bnd", analysis from my ExtensionOrder.lua puts it somewhere between veh and tgt. //have put it between des and tgt in line with init.lua RegisterPackageExtensionInfo call // RegisterPackageExtensionInfo call seems to mostly match my derived order in reverse - however clo doesnt fit the order and lng isn't in its table. //snakebite seems to honor packed order. List <string> fpkdExtensionsOrder = new List <string> { "fox2", "evf", "parts", "vfxlb", "vfx", "vfxlf", "veh", "frld", "des", "bnd", "tgt", "phsd", "ph", "sim", "clo", "fsd", "sdf", "lua", "lng" }; Dictionary <string, List <string> > filesByExtension = new Dictionary <string, List <string> >(); if (fpkFilesSorted.Count() > 1) { if (FpkType == "fpkd") { foreach (var fileName in fpkFilesSorted) { var extension = Path.GetExtension(fileName).Substring(1); List <string> extensionFiles = null; filesByExtension.TryGetValue(extension, out extensionFiles); if (extensionFiles == null) { extensionFiles = new List <string>(); filesByExtension.Add(extension, extensionFiles); } extensionFiles.Add(fileName); } //tex sorting by alphabetical just 'cause. I don't know if there's supposed to be some order within extension groupings. foreach (KeyValuePair <string, List <string> > entry in filesByExtension) { entry.Value.Sort(); } fpkFilesSorted = new List <string>(); foreach (var extension in fpkdExtensionsOrder) { List <string> extensionFiles = null; filesByExtension.TryGetValue(extension, out extensionFiles); if (extensionFiles != null) { foreach (var fileName in extensionFiles) { fpkFilesSorted.Add(fileName); } } } } } GzsLib.WriteFpkArchive(FpkFile, FpkFolder, fpkFilesSorted); return(fpkList); }