/// <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); }
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); }
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); }
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); }
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 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); }
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); }