private static List <string> GetFMSupportedLanguagesFromInstDir(string fmInstPath, bool earlyOutOnEnglish) { // Get initial list of base FM dirs the normal way: we don't want to count these as lang dirs even if // they're named such (matching FMSel behavior) var searchList = FastIO.GetDirsTopOnly(fmInstPath, "*", ignoreReparsePoints: true); if (searchList.Count == 0) { return(new List <string>()); } #region Move key dirs to end of list (priority) // Searching folders is horrendously slow, so prioritize folders most likely to contain lang dirs so // if we find English, we end up earlying-out much faster for (int i = 0; i < 3; i++) { string keyDir = i switch { 0 => "/books", 1 => "/intrface", _ => "/strings" }; for (int j = 0; j < searchList.Count; j++) { if (j < searchList.Count - 1 && searchList[j].PathEndsWithI(keyDir)) { string item = searchList[j]; searchList.RemoveAt(j); searchList.Add(item); break; } } } #endregion var langsFoundList = new HashSetI(Supported.Length); while (searchList.Count > 0) { string bdPath = searchList[searchList.Count - 1]; searchList.RemoveAt(searchList.Count - 1); bool englishFound = FastIO.SearchDirForLanguages(SupportedHash, bdPath, searchList, langsFoundList, earlyOutOnEnglish); // Matching FMSel behavior: early-out on English if (earlyOutOnEnglish && englishFound) { return new List <string> { "English" } } ; } return(SortLangsToSpec(langsFoundList)); }
private static void ClearCacheDir(FanMission fm) { string fmCachePath = Path.Combine(Paths.FMsCache, fm.InstalledDir); if (!fmCachePath.TrimEnd(CA_BS_FS).PathEqualsI(Paths.FMsCache.TrimEnd(CA_BS_FS)) && Directory.Exists(fmCachePath)) { try { foreach (string f in FastIO.GetFilesTopOnly(fmCachePath, "*")) { File.Delete(f); } foreach (string d in FastIO.GetDirsTopOnly(fmCachePath, "*")) { Directory.Delete(d, recursive: true); } } catch (Exception ex) { Log("Exception clearing files in FM cache for " + fm.Archive + " / " + fm.InstalledDir, ex); } } }
internal static void CreateOrClearTempPath(string path) { #region Safety check // Make sure we never delete any paths that are not safely tucked in our temp folder string baseTemp = _baseTemp.TrimEnd(CA_BS_FS_Space); // @DIRSEP: getting rid of this concat is more trouble than it's worth // This method is called rarely and only once in a row bool pathIsInTempDir = path.PathStartsWithI(baseTemp + "\\"); Misc.AssertR(pathIsInTempDir, "Path '" + path + "' is not in temp dir '" + baseTemp + "'"); if (!pathIsInTempDir) { return; } #endregion if (Directory.Exists(path)) { try { foreach (string f in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) { new FileInfo(f).IsReadOnly = false; } foreach (string d in Directory.GetDirectories(path, "*", SearchOption.AllDirectories)) { Misc.Dir_UnSetReadOnly(d); } } catch (Exception ex) { Log("Exception setting temp path subtree to all non-readonly.\r\n" + "path was: " + path, ex); } try { foreach (string f in FastIO.GetFilesTopOnly(path, "*")) { File.Delete(f); } foreach (string d in FastIO.GetDirsTopOnly(path, "*")) { Directory.Delete(d, recursive: true); } } catch (Exception ex) { Log("Exception clearing temp path " + path, ex); } } else { try { Directory.CreateDirectory(path); } catch (Exception ex) { Log("Exception creating temp path " + path, ex); } } }
internal static async Task BackupFM(FanMission fm, string fmInstalledPath, string fmArchivePath) { bool backupSavesAndScreensOnly = fmArchivePath.IsEmpty() || (Config.BackupFMData == BackupFMData.SavesAndScreensOnly && (fm.Game != Game.Thief3 || !Config.T3UseCentralSaves)); if (!GameIsKnownAndSupported(fm.Game)) { Log("Game type is unknown or unsupported (" + fm.Archive + ", " + fm.InstalledDir + ", " + fm.Game + ")", stackTrace: true); return; } await Task.Run(() => { if (backupSavesAndScreensOnly && fm.InstalledDir.IsEmpty()) { return; } string thisFMInstallsBasePath = Config.GetFMInstallPathUnsafe(fm.Game); string savesDir = fm.Game == Game.Thief3 ? _t3SavesDir : _darkSavesDir; string savesPath = Path.Combine(thisFMInstallsBasePath, fm.InstalledDir, savesDir); string netSavesPath = Path.Combine(thisFMInstallsBasePath, fm.InstalledDir, _darkNetSavesDir); // Screenshots directory name is the same for T1/T2/T3/SS2 string screensPath = Path.Combine(thisFMInstallsBasePath, fm.InstalledDir, _screensDir); string ss2CurrentPath = Path.Combine(thisFMInstallsBasePath, fm.InstalledDir, _ss2CurrentDir); string bakFile = Path.Combine(Config.FMsBackupPath, (!fm.Archive.IsEmpty() ? fm.Archive.RemoveExtension() : fm.InstalledDir) + Paths.FMBackupSuffix); if (backupSavesAndScreensOnly) { var savesAndScreensFiles = new List <string>(); if (Directory.Exists(savesPath)) { savesAndScreensFiles.AddRange(Directory.GetFiles(savesPath, "*", SearchOption.AllDirectories)); } if (Directory.Exists(netSavesPath)) { savesAndScreensFiles.AddRange(Directory.GetFiles(netSavesPath, "*", SearchOption.AllDirectories)); } if (Directory.Exists(screensPath)) { savesAndScreensFiles.AddRange(Directory.GetFiles(screensPath, "*", SearchOption.AllDirectories)); } if (fm.Game == Game.SS2) { savesAndScreensFiles.AddRange(Directory.GetFiles(ss2CurrentPath, "*", SearchOption.AllDirectories)); var ss2SaveDirs = FastIO.GetDirsTopOnly( Path.Combine(thisFMInstallsBasePath, fm.InstalledDir), "save_*"); foreach (string dir in ss2SaveDirs) { if (_ss2SaveDirsOnDiskRegex.IsMatch(dir)) { savesAndScreensFiles.AddRange(Directory.GetFiles(dir, "*", SearchOption.AllDirectories)); } } } if (savesAndScreensFiles.Count == 0) { return; } using var archive = new ZipArchive(new FileStream(bakFile, FileMode.Create, FileAccess.Write), ZipArchiveMode.Create, leaveOpen: false); foreach (string f in savesAndScreensFiles) { string fn = f.Substring(fmInstalledPath.Length).Trim(CA_BS_FS); AddEntry(archive, f, fn); } return; } string[] installedFMFiles = Directory.GetFiles(fmInstalledPath, "*", SearchOption.AllDirectories); var(changedList, addedList, fullList) = GetFMDiff(installedFMFiles, fmInstalledPath, fmArchivePath, fm.Game); // If >90% of files are different, re-run and use only size difference // They could have been extracted with NDL which uses SevenZipSharp and that one puts different // timestamps, when it puts the right ones at all if (changedList.Count > 0 && ((double)changedList.Count / fullList.Count) > 0.9) { (changedList, addedList, fullList) = GetFMDiff(installedFMFiles, fmInstalledPath, fmArchivePath, fm.Game, useOnlySize: true); } try { using var archive = new ZipArchive(new FileStream(bakFile, FileMode.Create, FileAccess.Write), ZipArchiveMode.Create, leaveOpen: false); foreach (string f in installedFMFiles) { string fn = f.Substring(fmInstalledPath.Length).Trim(CA_BS_FS); if (IsSaveOrScreenshot(fn, fm.Game) || (!fn.PathEqualsI(Paths.FMSelInf) && !fn.PathEqualsI(_startMisSav) && (changedList.PathContainsI(fn) || addedList.PathContainsI(fn)))) { AddEntry(archive, f, fn); } } string fmSelInfString = ""; for (int i = 0; i < fullList.Count; i++) { string f = fullList[i]; if (!installedFMFiles.PathContainsI(Path.Combine(fmInstalledPath, f))) { // @DIRSEP: Test if FMSel is dirsep-agnostic here. If so, remove the ToSystemDirSeps() fmSelInfString += _removeFileEq + f.ToSystemDirSeps() + "\r\n"; } } if (!fmSelInfString.IsEmpty()) { var entry = archive.CreateEntry(Paths.FMSelInf, CompressionLevel.Fastest); using var eo = entry.Open(); using var sw = new StreamWriter(eo, Encoding.UTF8); sw.Write(fmSelInfString); } } catch (Exception ex) { Log("Exception in zip archive create and/or write (" + fm.Archive + ", " + fm.InstalledDir + ", " + fm.Game + ")", ex); } }); }