private void FetchME2File() { BackupFile fileTofetch = SelectedME2File; SaveFileDialog m = new SaveFileDialog { Title = M3L.GetString(M3L.string_selectDestinationLocation), Filter = M3L.GetString(M3L.string_packageFile) + @"|*" + Path.GetExtension(fileTofetch.Filename), FileName = fileTofetch.Filename }; var result = m.ShowDialog(mainwindow); if (result.Value) { if (fileTofetch.Module == @"BASEGAME") { var fetchedfilestream = VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME2, fileTofetch.Filename); fetchedfilestream.WriteToFile(m.FileName); } else { var fetchedfilestream = VanillaDatabaseService.FetchME1ME2DLCFile(Mod.MEGame.ME2, fileTofetch.Module, fileTofetch.Filename); fetchedfilestream.WriteToFile(m.FileName); } } M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_fileFetchedAndWrittenToX, m.FileName), M3L.GetString(M3L.string_fileFetched)); }
private void LoadME2FilesList() { var me2files = new List <BackupFile>(); var bup = Utilities.GetGameBackupPath(Mod.MEGame.ME2); if (bup != null) { var target = new GameTarget(Mod.MEGame.ME2, bup, false); var cookedPath = MEDirectories.CookedPath(target); foreach (var f in Extensions.GetFiles(cookedPath, @"\.pcc|\.tfc|\.afc|\.bin|\.tlk", SearchOption.AllDirectories)) { me2files.Add(new BackupFile(@"BASEGAME", Path.GetFileName(f))); } var dlcDir = MEDirectories.DLCPath(target); var officialDLC = VanillaDatabaseService.GetInstalledOfficialDLC(target); foreach (var v in officialDLC) { var cookedDLCPath = Path.Combine(dlcDir, v, @"CookedPC"); if (Directory.Exists(cookedDLCPath)) { foreach (var f in Directory.EnumerateFiles(cookedDLCPath, @"*.pcc", SearchOption.TopDirectoryOnly)) { me2files.Add(new BackupFile(v, Path.GetFileName(f))); } } } } Application.Current.Dispatcher.Invoke(delegate { ME2Files.ReplaceAll(me2files); }); Debug.WriteLine(@"Num ME2 files: " + ME2Files.Count); }
public override void OnPanelVisible() { var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target); var uiModInstalled = installedDLCMods.Intersect(DLCUIModFolderNames).Any(); if (uiModInstalled) { var result = Xceed.Wpf.Toolkit.MessageBox.Show(window, "A GUI compatibility mod is used to make SP Controller Support, Interface Scaling Mod and Interface Scaling Add-on work with mods that modify the same files or include their own interface files. " + "This is a critical step when using UI mods or you may experience softlocks or incorrect interfaces on screen.\n\n" + "Generate a GUI compatibility pack ONLY when you have installed all other non-texture mods as it is built against your current installation.\n\nGenerate compatibility pack?", "Confirm generation", MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.No) { OnClosing(DataEventArgs.Empty); return; } StartGuiCompatibilityScanner(); } else { Xceed.Wpf.Toolkit.MessageBox.Show(window, "No UI mods are installed. GUI compatibility generator only works with SP Controller Mod, Interface Scaling Mod, and Interface Scaling Add-On.", "No UI mods installed", MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); } }
private void LoadME3FilesList() { var me3files = new List <BackupFile>(); var bup = BackupService.GetGameBackupPath(Mod.MEGame.ME3); if (bup != null) { var target = new GameTarget(Mod.MEGame.ME3, bup, false); var cookedPath = MEDirectories.CookedPath(target); foreach (var f in Extensions.GetFiles(cookedPath, @"\.pcc|\.tfc|\.afc|\.bin|\.tlk", SearchOption.AllDirectories)) { me3files.Add(new BackupFile(@"BASEGAME", Path.GetFileName(f))); } me3files.Sort(); //sort basegame var dlcDir = MEDirectories.DLCPath(target); var officialDLC = VanillaDatabaseService.GetInstalledOfficialDLC(target); foreach (var v in officialDLC) { var sfarPath = Path.Combine(dlcDir, v, @"CookedPCConsole", @"Default.sfar"); if (File.Exists(sfarPath)) { var filesToAdd = new List <BackupFile>(); DLCPackage dlc = new DLCPackage(sfarPath); foreach (var f in dlc.Files) { filesToAdd.Add(new BackupFile(v, Path.GetFileName(f.FileName))); } filesToAdd.Sort(); me3files.AddRange(filesToAdd); } } //TESTPATCH var tpPath = ME3Directory.GetTestPatchPath(target); if (File.Exists(tpPath)) { var filesToAdd = new List <BackupFile>(); DLCPackage dlc = new DLCPackage(tpPath); foreach (var f in dlc.Files) { filesToAdd.Add(new BackupFile(@"TESTPATCH", Path.GetFileName(f.FileName))); } filesToAdd.Sort(); me3files.AddRange(filesToAdd); } } Application.Current.Dispatcher.Invoke(delegate { ME3Files.ReplaceAll(me3files); }); Debug.WriteLine(@"Num ME3 files: " + ME3Files.Count); }
public bool RevalidateIsModified(bool notify = true) { bool _isModified = IsModified; IsModified = !VanillaDatabaseService.IsFileVanilla(target, Path.Combine(target.TargetPath, FilePath)); if (!IsModified && _isModified && notify) { //Debug.WriteLine("Notifying that " + FilePath + " is no longer modified."); notifyNoLongerModified?.Invoke(this); } return(IsModified); }
public static void UpdateMD5Map(MEGame game, string directory, string outName) { var outF = $@"C:\Users\mgame\source\repos\ME3Tweaks\MassEffectModManager\MassEffectModManagerCore\modmanager\gamemd5\{outName}"; var db = VanillaDatabaseService.LoadDatabaseFor(game, false); if (game.IsLEGame()) { db.RemoveAll(x => x.Key.Contains(@"BioGame\Config")); // Do not include config files } MemoryStream mapStream = new MemoryStream(); // Name Table mapStream.WriteInt32(db.Count); // Num Entries foreach (var f in db.Keys) { mapStream.WriteStringASCIINull(f); } // Data Table mapStream.WriteInt32(db.Count); int idx = 0; foreach (var f in db) { mapStream.WriteInt32(idx); // Name Table IDX. Update this code for duplicates support mapStream.WriteInt32(f.Value[0].size); // Size var md5 = f.Value[0].md5; for (int i = 0; i < 32; i++) { byte b = 0; b |= HexToInt(md5[i]); b = (byte)(b << 4); i++; b |= HexToInt(md5[i]); mapStream.WriteByte(b); } idx++; } var compBytes = LZMA.Compress(mapStream.ToArray()); MemoryStream ms = new MemoryStream(); ms.WriteStringASCII(@"MD5T"); ms.WriteInt32((int)mapStream.Length); ms.Write(compBytes); ms.WriteToFile(outF); }
public override void OnPanelVisible() { var db = VanillaDatabaseService.LoadDatabaseFor(Mod.MEGame.ME1, false); ME1Files.ReplaceAll(getDBItems(db)); db = VanillaDatabaseService.LoadDatabaseFor(Mod.MEGame.ME2, false); ME2Files.ReplaceAll(getDBItems(db)); db = VanillaDatabaseService.LoadDatabaseFor(Mod.MEGame.ME3, false); ME3Files.ReplaceAll(getDBItems(db)); LoadingInProgress = false; ME1FilesView.Filter = FilterBackupFilesME1; ME2FilesView.Filter = FilterBackupFilesME2; ME3FilesView.Filter = FilterBackupFilesME3; }
public void ReloadGameTarget() { if (Game != Mod.MEGame.Unknown && !IsCustomOption) { var oldTMOption = TextureModded; var alotInfo = GetInstalledALOTInfo(); if (alotInfo != null) { TextureModded = true; ALOTVersion = alotInfo.ToString(); if (alotInfo.MEUITMVER > 0) { MEUITMInstalled = true; MEUITMVersion = alotInfo.MEUITMVER; } } else { TextureModded = false; ALOTVersion = null; MEUITMInstalled = false; MEUITMVersion = 0; } Log.Information(@"Getting game source for target " + TargetPath); var hashCheckResult = VanillaDatabaseService.GetGameSource(this); GameSource = hashCheckResult.result; ExecutableHash = hashCheckResult.hash; if (GameSource == null) { Log.Error(@"Unknown source or illegitimate installation: " + hashCheckResult.hash); } else { Log.Information(@"Source: " + GameSource); } IsPolishME1 = Game == Mod.MEGame.ME1 && File.Exists(Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Movies", @"niebieska_pl.bik")); if (IsPolishME1) { Log.Information(@"ME1 Polish Edition detected"); } if (RegistryActive && Settings.AutoUpdateLODs && oldTMOption != TextureModded) { UpdateLODs(); } } }
public void PopulateModifiedBasegameFiles(Func <string, bool> restoreBasegamefileConfirmationCallback, Func <string, bool> restoreSfarConfirmationCallback, Action notifySFARRestoringCallback, Action notifyFileRestoringCallback, Action <object> notifyRestoredCallback) { ModifiedBasegameFiles.ClearEx(); ModifiedSFARFiles.ClearEx(); List <string> modifiedSfars = new List <string>(); List <string> modifiedFiles = new List <string>(); void failedCallback(string file) { if (file.EndsWith(@".sfar")) { modifiedSfars.Add(file); return; } if (file == getALOTMarkerFilePath()) { return; //Do not report this file as modified or user will desync game state with texture state } modifiedFiles.Add(file); } VanillaDatabaseService.ValidateTargetAgainstVanilla(this, failedCallback); List <string> inconsistentDLC = new List <string>(); VanillaDatabaseService.ValidateTargetDLCConsistency(this, x => inconsistentDLC.Add(x)); modifiedSfars.AddRange(inconsistentDLC.Select(x => Path.Combine(x, "CookedPCConsole", "Default.sfar"))); modifiedSfars = modifiedSfars.Distinct().ToList(); //filter out if modified + inconsistent ModifiedSFARFiles.AddRange(modifiedSfars.Select(file => new SFARObject(file, this, restoreSfarConfirmationCallback, notifySFARRestoringCallback, notifyRestoredCallback))); ModifiedBasegameFiles.AddRange(modifiedFiles.Select(file => new ModifiedFileObject(file.Substring(TargetPath.Length + 1), this, restoreBasegamefileConfirmationCallback, notifyFileRestoringCallback, notifyRestoredCallback))); }
public static Dictionary <string, int> GetMountPriorities(GameTarget selectedTarget) { //make dictionary from basegame files var dlcmods = VanillaDatabaseService.GetInstalledDLCMods(selectedTarget); var mountMapping = new Dictionary <string, int>(); foreach (var dlc in dlcmods) { var mountpath = Path.Combine(MEDirectories.DLCPath(selectedTarget), dlc); try { mountMapping[dlc] = MELoadedFiles.GetMountPriority(mountpath, selectedTarget.Game); } catch (Exception e) { Log.Error($"Exception getting mount priority from file: {mountpath}: {e.Message}"); } } return(mountMapping); }
private void LoadME1FilesList() { var me1files = new List <BackupFile>(); var bup = BackupService.GetGameBackupPath(Mod.MEGame.ME1); if (bup != null) { var target = new GameTarget(Mod.MEGame.ME1, bup, false); var cookedPath = MEDirectories.CookedPath(target); foreach (var f in Extensions.GetFiles(cookedPath, @"\.u|\.upk|\.sfm", SearchOption.AllDirectories)) { me1files.Add(new BackupFile(@"BASEGAME", Path.GetFileName(f))); } me1files.Sort(); //sort basegame var dlcDir = MEDirectories.DLCPath(target); var officialDLC = VanillaDatabaseService.GetInstalledOfficialDLC(target); foreach (var v in officialDLC) { var cookedDLCPath = Path.Combine(dlcDir, v, @"CookedPC"); if (Directory.Exists(cookedDLCPath)) { var filesToAdd = new List <BackupFile>(); foreach (var f in Extensions.GetFiles(cookedDLCPath, @"\.u|\.upk|\.sfm", SearchOption.AllDirectories)) { filesToAdd.Add(new BackupFile(v, Path.GetFileName(f))); } filesToAdd.Sort(); me1files.AddRange(filesToAdd); } } } Application.Current.Dispatcher.Invoke(delegate { ME1Files.ReplaceAll(me1files); }); Debug.WriteLine(@"Num ME1 files: " + ME2Files.Count); }
public void OnSelectedTargetChanged() { InstalledDLCs.ClearEx(); if (SelectedTarget != null) { // maps DLC folder name -> mount number var installedDlc = VanillaDatabaseService.GetInstalledOfficialDLC(SelectedTarget, true); foreach (var dlc in installedDlc) { Debug.WriteLine(dlc); var foldername = dlc.TrimStart('x'); InstalledDLCs.Add(new InstalledDLC() { target = SelectedTarget, DLCFolderName = dlc, UIDLCFolderName = foldername, Enabled = !dlc.StartsWith('x'), HumanName = ThirdPartyServices.GetThirdPartyModInfo(foldername, SelectedTarget.Game).modname }); } } }
public GameTarget(Mod.MEGame game, string target, bool currentRegistryActive, bool isCustomOption = false) { this.Game = game; this.RegistryActive = currentRegistryActive; this.IsCustomOption = isCustomOption; this.TargetPath = target.TrimEnd('\\'); if (game != Mod.MEGame.Unknown && !IsCustomOption) { var alotInfo = GetInstalledALOTInfo(); if (alotInfo != null) { ALOTInstalled = true; ALOTVersion = alotInfo.ToString(); if (alotInfo.MEUITMVER > 0) { MEUITMInstalled = true; MEUITMVersion = alotInfo.MEUITMVER; } } Log.Information(@"Getting game source for target " + TargetPath); var hashCheckResult = VanillaDatabaseService.GetGameSource(this); GameSource = hashCheckResult.result; if (GameSource == null) { Log.Error(@"Unknown source or illegitimate installation: " + hashCheckResult.hash); } else { Log.Information(@"Source: " + GameSource); } IsPolishME1 = game == Mod.MEGame.ME1 && File.Exists(Path.Combine(target, @"BioGame", @"CookedPC", @"Movies", @"niebieska_pl.bik")); if (IsPolishME1) { Log.Information(@"ME1 Polish Edition detected"); } } }
public override void OnPanelVisible() { var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target); var uiModInstalled = installedDLCMods.Intersect(DLCUIModFolderNames).Any(); if (uiModInstalled) { var result = M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogWhatGuiCompatPackIsFor), M3L.GetString(M3L.string_confirmGeneration), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.No) { OnClosing(DataEventArgs.Empty); return; } StartGuiCompatibilityScanner(); } else { Log.Information(@"No UI mods are installed. Cannot run GUI Compat Generator"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogNoUiModsAreInstalled), M3L.GetString(M3L.string_noUiModsAreInstalled), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); } }
public void PopulateModifiedBasegameFiles(Func <string, bool> restoreBasegamefileConfirmationCallback, Func <string, bool> restoreSfarConfirmationCallback, Action notifySFARRestoringCallback, Action notifyFileRestoringCallback, Action <object> notifyRestoredCallback) { ModifiedBasegameFiles.ClearEx(); ModifiedSFARFiles.ClearEx(); void failedCallback(string file) { //todo: Filter out SFARs? if (file.EndsWith(@".sfar")) { ModifiedSFARFiles.Add(new SFARObject(file, this, restoreSfarConfirmationCallback, notifySFARRestoringCallback, notifyRestoredCallback)); return; } ModifiedBasegameFiles.Add(new ModifiedFileObject(file.Substring(TargetPath.Length + 1), this, restoreBasegamefileConfirmationCallback, notifyFileRestoringCallback, notifyRestoredCallback)); } VanillaDatabaseService.ValidateTargetAgainstVanilla(this, failedCallback); }
private void BeginBackup() { if (Utilities.IsGameRunning(BackupSourceTarget.Game)) { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_cannotBackupGameWhileRunning, Utilities.GetGameName(BackupSourceTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return; } NamedBackgroundWorker bw = new NamedBackgroundWorker(Game.ToString() + @"Backup"); bw.DoWork += (a, b) => { BackupInProgress = true; List <string> nonVanillaFiles = new List <string>(); void nonVanillaFileFoundCallback(string filepath) { Log.Error($@"Non-vanilla file found: {filepath}"); nonVanillaFiles.Add(filepath); } List <string> inconsistentDLC = new List <string>(); void inconsistentDLCFoundCallback(string filepath) { if (BackupSourceTarget.Supported) { Log.Error($@"DLC is in an inconsistent state: {filepath}"); inconsistentDLC.Add(filepath); } else { Log.Error(@"Detected an inconsistent DLC, likely due to an unofficial copy of the game"); } } ProgressVisible = true; ProgressIndeterminate = true; BackupStatus = M3L.GetString(M3L.string_validatingBackupSource); VanillaDatabaseService.LoadDatabaseFor(Game); bool isVanilla = VanillaDatabaseService.ValidateTargetAgainstVanilla(BackupSourceTarget, nonVanillaFileFoundCallback); bool isDLCConsistent = VanillaDatabaseService.ValidateTargetDLCConsistency(BackupSourceTarget, inconsistentDLCCallback: inconsistentDLCFoundCallback); List <string> dlcModsInstalled = VanillaDatabaseService.GetInstalledDLCMods(BackupSourceTarget); if (isVanilla && isDLCConsistent && dlcModsInstalled.Count == 0) { BackupStatus = M3L.GetString(M3L.string_waitingForUserInput); string backupPath = null; bool end = false; Application.Current.Dispatcher.Invoke(delegate { Log.Error(@"Prompting user to select backup destination"); CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = M3L.GetString(M3L.string_selectBackupDestination) }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { //Check empty backupPath = m.FileName; if (Directory.Exists(backupPath)) { if (Directory.GetFiles(backupPath).Length > 0 || Directory.GetDirectories(backupPath).Length > 0) { //Directory not empty Log.Error(@"Selected backup directory is not empty."); M3L.ShowDialog(window, M3L.GetString(M3L.string_directoryIsNotEmptyMustBeEmpty), M3L.GetString(M3L.string_directoryNotEmpty), MessageBoxButton.OK, MessageBoxImage.Error); end = true; EndBackup(); return; } } } else { end = true; EndBackup(); return; } }); if (end) { return; } #region callbacks void fileCopiedCallback() { ProgressValue++; } string dlcFolderpath = MEDirectories.DLCPath(BackupSourceTarget) + '\\'; int dlcSubStringLen = dlcFolderpath.Length; bool aboutToCopyCallback(string file) { if (file.Contains(@"\cmmbackup\")) { return(false); //do not copy cmmbackup files } if (file.StartsWith(dlcFolderpath)) { //It's a DLC! string dlcname = file.Substring(dlcSubStringLen); dlcname = dlcname.Substring(0, dlcname.IndexOf('\\')); if (MEDirectories.OfficialDLCNames(BackupSourceTarget.Game).TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, hrName); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, dlcname); } } else { //It's basegame if (file.EndsWith(@".bik")) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.string_movies); } else if (new FileInfo(file).Length > 52428800) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, Path.GetFileName(file)); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.string_basegame); } } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = M3L.GetString(M3L.string_creatingBackup); CopyDir.CopyAll_ProgressBar(new DirectoryInfo(BackupSourceTarget.TargetPath), new DirectoryInfo(backupPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { @"*.pdf", @"*.mp3" }); switch (Game) { case Mod.MEGame.ME1: case Mod.MEGame.ME2: Utilities.WriteRegistryKey(App.BACKUP_REGISTRY_KEY, Game + @"VanillaBackupLocation", backupPath); break; case Mod.MEGame.ME3: Utilities.WriteRegistryKey(App.REGISTRY_KEY_ME3CMM, @"VanillaCopyLocation", backupPath); break; } Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Success" } }); EndBackup(); return; } if (!isVanilla) { //Show UI for non vanilla Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Game modified" } }); b.Result = (nonVanillaFiles, M3L.GetString(M3L.string_cannotBackupModifiedGame), M3L.GetString(M3L.string_followingFilesDoNotMatchTheVanillaDatabase)); } else if (!isDLCConsistent) { Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, DLC inconsistent" } }); if (BackupSourceTarget.Supported) { b.Result = (inconsistentDLC, M3L.GetString(M3L.string_inconsistentDLCDetected), M3L.GetString(M3L.string_dialogTheFollowingDLCAreInAnInconsistentState)); } else { b.Result = (M3L.GetString(M3L.string_inconsistentDLCDetected), M3L.GetString(M3L.string_inconsistentDLCDetectedUnofficialGame)); } } else if (dlcModsInstalled.Count > 0) { Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, DLC mods found" } }); b.Result = (dlcModsInstalled, M3L.GetString(M3L.string_dlcModsAreInstalled), M3L.GetString(M3L.string_dialogDLCModsWereDetectedCannotBackup)); } EndBackup(); }; bw.RunWorkerCompleted += (a, b) => { if (b.Result is (List <string> listItems, string title, string text)) { ListDialog ld = new ListDialog(listItems, title, text, window); ld.Show(); } else if (b.Result is (string errortitle, string message)) { M3L.ShowDialog(window, message, errortitle, MessageBoxButton.OK, MessageBoxImage.Error); } CommandManager.InvalidateRequerySuggested(); }; bw.RunWorkerAsync(); }
private void StartGuiCompatibilityScanner() { NamedBackgroundWorker bw = new NamedBackgroundWorker(@"GUICompatibilityScanner"); bw.DoWork += (a, b) => { Percent = 0; ActionString = M3L.GetString(M3L.string_preparingCompatGenerator); ActionSubstring = M3L.GetString(M3L.string_pleaseWait); var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target); var numTotalDLCMods = installedDLCMods.Count; var uiModInstalled = installedDLCMods.Intersect(DLCUIModFolderNames).Any(); var dlcRoot = MEDirectories.DLCPath(target); if (uiModInstalled) { var nonUIinstalledDLCMods = installedDLCMods.Except(DLCUIModFolderNamesIncludingPatch).ToList(); if (nonUIinstalledDLCMods.Count < numTotalDLCMods && nonUIinstalledDLCMods.Count > 0) { //Get UI library bool xbxLibrary = installedDLCMods.Contains(@"DLC_CON_XBX"); bool uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling"); if (!xbxLibrary && !uiscalinglibrary) { uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling_Shared"); } if (xbxLibrary && uiscalinglibrary) { //can't have both! Not supported. Application.Current.Dispatcher.Invoke(delegate { Log.Error(@"Cannot make compat pack: Both ISM and SP Controller are installed, this is not supported."); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCannotGenerateCompatPackInvalidConfig), M3L.GetString(M3L.string_invalidConfiguration), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); }); b.Result = GUICompatibilityThreadResult.INVALID_UI_MOD_CONFIG; return; } void progressCallback(long done, long total) { ActionString = M3L.GetString(M3L.string_downloadingUiLibrary); ActionSubstring = xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling"; Percent = getPercent(done, total); } var uiLibraryPath = GetUILibraryPath(xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling", true, progressCallback); if (uiLibraryPath == null) { Log.Error(@"Required UI library could not be downloaded."); Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_cannotGeneratorCompatPackCouldNotDownload), M3L.GetString(M3L.string_couldNotAcquireUiLibrary), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); }); b.Result = GUICompatibilityThreadResult.NO_UI_LIBRARY; return; } //Open UI library SevenZipExtractor libraryArchive = new SevenZipExtractor(uiLibraryPath); List <string> libraryGUIs = libraryArchive.ArchiveFileData.Where(x => !x.IsDirectory).Select(x => x.FileName.Substring(Path.GetFileNameWithoutExtension(uiLibraryPath).Length + 1)).Select(x => x.Substring(0, x.Length - 4)).ToList(); //remove / on end too //We have UI mod(s) installed and at least one other DLC mod. var supercedanceList = getFileSupercedances().Where(x => x.Value.Any(x => !DLCUIModFolderNamesIncludingPatch.Contains(x))).ToDictionary(p => p.Key, p => p.Value); //Find GUIs ConcurrentDictionary <string, string> filesToBePatched = new ConcurrentDictionary <string, string>(); //Dictionary because there is no ConcurrentList. Keys and values are idenitcal. ActionString = M3L.GetString(M3L.string_scanningForGuiExports); ActionSubstring = M3L.GetString(M3L.string_pleaseWait); Percent = 0; int done = 0; string singlesuffix = M3L.GetString(M3L.string_singularFile); string pluralsuffix = M3L.GetString(M3L.string_pluralFiles); Parallel.ForEach(supercedanceList, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, (pair) => { var firstNonUIModDlc = pair.Value.FirstOrDefault(x => !DLCUIModFolderNamesIncludingPatch.Contains(x)); if (firstNonUIModDlc != null) { //Scan file. var packagefile = Path.Combine(dlcRoot, firstNonUIModDlc, target.Game == MEGame.ME3 ? @"CookedPCConsole" : @"CookedPC", pair.Key); Log.Information(@"Scanning file for GFXMovieInfo exports: " + packagefile); if (!File.Exists(packagefile)) { throw new Exception($@"Package file for inspecting GUIs in was not found: {packagefile}"); } var package = MEPackageHandler.OpenMEPackage(packagefile); var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList(); if (guiExports.Count > 0) { //potential item needing replacement //Check GUI library to see if we have anything. foreach (var export in guiExports) { if (libraryGUIs.Contains(export.GetFullPath, StringComparer.InvariantCultureIgnoreCase)) { //match filesToBePatched[packagefile] = packagefile; ActionSubstring = M3L.GetString(M3L.string_interp_XFilesNeedToBePatched, filesToBePatched.Count.ToString(), filesToBePatched.Count == 1 ? singlesuffix : pluralsuffix); Log.Information($@"{firstNonUIModDlc} {pair.Key} has GUI export that is in UI library, marking for patching. Trigger: {export.GetFullPath}"); break; } } } } Interlocked.Increment(ref done); Percent = getPercent(done, supercedanceList.Count); }); if (filesToBePatched.Count > 0) { Log.Information(@"A GUI compatibility patch is required for this game configuration"); b.Result = GUICompatibilityThreadResult.REQUIRED; var generatedMod = GenerateCompatibilityPackForFiles(nonUIinstalledDLCMods, filesToBePatched.Keys.ToList(), libraryArchive); b.Result = GUICompatibilityThreadResult.GENERATED_PACK; Application.Current.Dispatcher.Invoke(delegate { ((MainWindow)window).LoadMods(generatedMod); }); //reload to this mod } } Log.Information(@"A GUI compatibility patch is not required for this game configuration"); b.Result = GUICompatibilityThreadResult.NOT_REQUIRED; } else { Log.Information(@"No UI mods are installed - no GUI compatibility pack required"); b.Result = GUICompatibilityThreadResult.NO_UI_MODS_INSTALLED; } }; bw.RunWorkerCompleted += (a, b) => { if (b.Result is GUICompatibilityThreadResult gctr) { Analytics.TrackEvent(@"Generated a UI compatibility pack", new Dictionary <string, string>() { { @"Result", gctr.ToString() } }); OnClosing(DataEventArgs.Empty); } else { throw new Exception(@"GUI Compatibility generator thread did not return a result! Please report this to ME3Tweaks"); } }; bw.RunWorkerAsync(); }
private void BeginBackup() { var targetToBackup = BackupSourceTarget; if (!targetToBackup.IsCustomOption) { if (Utilities.IsGameRunning(targetToBackup.Game)) { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_cannotBackupGameWhileRunning, Utilities.GetGameName(BackupSourceTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return; } } else { // Point to existing game installation Log.Information(@"BeginBackup() with IsCustomOption."); var linkWarning = M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_linkTargetWontBeModdable), M3L.GetString(M3L.string_linkWarning), MessageBoxButton.OKCancel, MessageBoxImage.Warning); if (linkWarning == MessageBoxResult.Cancel) { Log.Information(@"User aborted linking due to dialog"); return; } Log.Information(@"Prompting user to select executable of link target"); var gameexe = Utilities.PromptForGameExecutable(new[] { Game }); if (gameexe == null) { return; } targetToBackup = new GameTarget(Game, Utilities.GetGamePathFromExe(Game, gameexe), false, true); if (AvailableBackupSources.Any(x => x.TargetPath.Equals(targetToBackup.TargetPath, StringComparison.InvariantCultureIgnoreCase))) { // Can't point to an existing modding target Log.Error(@"This target is not valid to point to as a backup: It is listed a modding target already, it must be removed as a target first"); M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialog_linkFailedAlreadyATarget), M3L.GetString(M3L.string_cannotLinkGameCopy), MessageBoxButton.OK, MessageBoxImage.Error); return; } var validationFailureReason = targetToBackup.ValidateTarget(ignoreCmmVanilla: true); if (!targetToBackup.IsValid) { Log.Error(@"This installation is not valid to point to as a backup: " + validationFailureReason); M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialog_linkFailedInvalidTarget, validationFailureReason), M3L.GetString(M3L.string_invalidGameCopy), MessageBoxButton.OK, MessageBoxImage.Error); return; } } NamedBackgroundWorker nbw = new NamedBackgroundWorker(Game + @"Backup"); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { window.TaskBarItemInfoHandler.ProgressValue = d; } else if (b.UserState is TaskbarItemProgressState tbs) { window.TaskBarItemInfoHandler.ProgressState = tbs; } }; nbw.DoWork += (a, b) => { Log.Information(@"Starting the backup thread. Checking path: " + targetToBackup.TargetPath); BackupInProgress = true; bool end = false; List <string> nonVanillaFiles = new List <string>(); void nonVanillaFileFoundCallback(string filepath) { Log.Error($@"Non-vanilla file found: {filepath}"); nonVanillaFiles.Add(filepath); } List <string> inconsistentDLC = new List <string>(); void inconsistentDLCFoundCallback(string filepath) { if (targetToBackup.Supported) { Log.Error($@"DLC is in an inconsistent state: {filepath}"); inconsistentDLC.Add(filepath); } else { Log.Error(@"Detected an inconsistent DLC, likely due to an unofficial copy of the game"); } } ProgressVisible = true; ProgressIndeterminate = true; BackupStatus = M3L.GetString(M3L.string_validatingBackupSource); Log.Information(@"Checking target is vanilla"); bool isVanilla = VanillaDatabaseService.ValidateTargetAgainstVanilla(targetToBackup, nonVanillaFileFoundCallback); Log.Information(@"Checking DLC consistency"); bool isDLCConsistent = VanillaDatabaseService.ValidateTargetDLCConsistency(targetToBackup, inconsistentDLCCallback: inconsistentDLCFoundCallback); Log.Information(@"Checking only vanilla DLC is installed"); List <string> dlcModsInstalled = VanillaDatabaseService.GetInstalledDLCMods(targetToBackup).Select(x => { var tpmi = ThirdPartyServices.GetThirdPartyModInfo(x, targetToBackup.Game); if (tpmi != null) { return($@"{x} ({tpmi.modname})"); } return(x); }).ToList(); List <string> installedDLC = VanillaDatabaseService.GetInstalledOfficialDLC(targetToBackup); List <string> allOfficialDLC = MEDirectories.OfficialDLC(targetToBackup.Game); if (installedDLC.Count() < allOfficialDLC.Count()) { var dlcList = string.Join("\n - ", allOfficialDLC.Except(installedDLC).Select(x => $@"{MEDirectories.OfficialDLCNames(targetToBackup.Game)[x]} ({x})")); //do not localize dlcList = @" - " + dlcList; Log.Information(@"The following dlc will be missing in the backup if user continues: " + dlcList); Application.Current.Dispatcher.Invoke(delegate { var cancelDueToNotAllDLC = M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_notAllDLCInstalled, dlcList), M3L.GetString(M3L.string_someDlcNotInstalled), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (cancelDueToNotAllDLC == MessageBoxResult.No) { end = true; EndBackup(); return; } }); } if (end) { return; } if (isVanilla && isDLCConsistent && dlcModsInstalled.Count == 0) { BackupStatus = M3L.GetString(M3L.string_waitingForUserInput); string backupPath = null; if (!targetToBackup.IsCustomOption) { // Creating a new backup nbw.ReportProgress(0, TaskDialogProgressBarState.Paused); Application.Current.Dispatcher.Invoke(delegate { Log.Information(@"Prompting user to select backup destination"); CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = M3L.GetString(M3L.string_selectBackupDestination) }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { backupPath = m.FileName; Log.Information(@"Backup path chosen: " + backupPath); bool okToBackup = validateBackupPath(backupPath, targetToBackup); if (!okToBackup) { end = true; EndBackup(); return; } } else { end = true; EndBackup(); return; } }); if (end) { return; } nbw.ReportProgress(0, TaskbarItemProgressState.Indeterminate); } else { Log.Information(@"Linking existing backup at " + targetToBackup.TargetPath); backupPath = targetToBackup.TargetPath; // Linking existing backup Application.Current.Dispatcher.Invoke(delegate { bool okToBackup = validateBackupPath(targetToBackup.TargetPath, targetToBackup); if (!okToBackup) { end = true; EndBackup(); return; } }); } if (end) { return; } if (!targetToBackup.IsCustomOption) { #region callbacks and copy code // Copy to new backup void fileCopiedCallback() { ProgressValue++; if (ProgressMax > 0) { nbw.ReportProgress(0, ProgressValue * 1.0 / ProgressMax); } } string dlcFolderpath = MEDirectories.DLCPath(targetToBackup) + '\\'; int dlcSubStringLen = dlcFolderpath.Length; bool aboutToCopyCallback(string file) { try { if (file.Contains(@"\cmmbackup\")) { return(false); //do not copy cmmbackup files } if (file.StartsWith(dlcFolderpath, StringComparison.InvariantCultureIgnoreCase)) { //It's a DLC! string dlcname = file.Substring(dlcSubStringLen); var dlcFolderNameEndPos = dlcname.IndexOf('\\'); if (dlcFolderNameEndPos > 0) { dlcname = dlcname.Substring(0, dlcFolderNameEndPos); if (MEDirectories.OfficialDLCNames(targetToBackup.Game) .TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, hrName); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, dlcname); } } else { // Loose files in the DLC folder BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.GetString(M3L.string_basegame)); } } else { //It's basegame if (file.EndsWith(@".bik")) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.GetString(M3L.string_movies)); } else if (new FileInfo(file).Length > 52428800) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, Path.GetFileName(file)); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.GetString(M3L.string_basegame)); } } } catch (Exception e) { Crashes.TrackError(e, new Dictionary <string, string>() { { @"dlcFolderpath", dlcFolderpath }, { @"dlcSubStringLen", dlcSubStringLen.ToString() }, { @"file", file } }); } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; nbw.ReportProgress(0, TaskbarItemProgressState.Normal); } BackupStatus = M3L.GetString(M3L.string_creatingBackup); Log.Information($@"Backing up {targetToBackup.TargetPath} to {backupPath}"); nbw.ReportProgress(0, TaskbarItemProgressState.Normal); CopyDir.CopyAll_ProgressBar(new DirectoryInfo(targetToBackup.TargetPath), new DirectoryInfo(backupPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { @"*.pdf", @"*.mp3" }); #endregion } // Write key switch (Game) { case Mod.MEGame.ME1: case Mod.MEGame.ME2: Utilities.WriteRegistryKey(App.BACKUP_REGISTRY_KEY, Game + @"VanillaBackupLocation", backupPath); break; case Mod.MEGame.ME3: Utilities.WriteRegistryKey(App.REGISTRY_KEY_ME3CMM, @"VanillaCopyLocation", backupPath); break; } var cmmvanilla = Path.Combine(backupPath, @"cmm_vanilla"); if (!File.Exists(cmmvanilla)) { Log.Information($@"Writing cmm_vanilla to " + cmmvanilla); File.Create(cmmvanilla).Close(); } Log.Information($@"Backup completed."); Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Success" }, { @"Type", targetToBackup.IsCustomOption ? @"Linked" : @"Copy" } }); EndBackup(); return; } if (!isVanilla) { //Show UI for non vanilla Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Game modified" } }); b.Result = (nonVanillaFiles, M3L.GetString(M3L.string_cannotBackupModifiedGame), M3L.GetString(M3L.string_followingFilesDoNotMatchTheVanillaDatabase)); } else if (!isDLCConsistent) { Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, DLC inconsistent" } }); if (targetToBackup.Supported) { b.Result = (inconsistentDLC, M3L.GetString(M3L.string_inconsistentDLCDetected), M3L.GetString(M3L.string_dialogTheFollowingDLCAreInAnInconsistentState)); } else { b.Result = (M3L.GetString(M3L.string_inconsistentDLCDetected), M3L.GetString(M3L.string_inconsistentDLCDetectedUnofficialGame)); } } else if (dlcModsInstalled.Count > 0) { Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, DLC mods found" } }); b.Result = (dlcModsInstalled, M3L.GetString(M3L.string_dlcModsAreInstalled), M3L.GetString(M3L.string_dialogDLCModsWereDetectedCannotBackup)); } EndBackup(); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occured in {nbw.Name} thread: {b.Error.Message}"); } window.TaskBarItemInfoHandler.ProgressState = TaskbarItemProgressState.None; if (b.Result is (List <string> listItems, string title, string text)) { ListDialog ld = new ListDialog(listItems, title, text, window); ld.Show(); } else if (b.Result is (string errortitle, string message)) { M3L.ShowDialog(window, message, errortitle, MessageBoxButton.OK, MessageBoxImage.Error); } CommandManager.InvalidateRequerySuggested(); }; nbw.RunWorkerAsync(); }
private void BeginBackup() { if (Utilities.IsGameRunning(BackupSourceTarget.Game)) { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_cannotBackupGameWhileRunning, Utilities.GetGameName(BackupSourceTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return; } NamedBackgroundWorker bw = new NamedBackgroundWorker(Game.ToString() + @"Backup"); bw.DoWork += (a, b) => { BackupInProgress = true; List <string> nonVanillaFiles = new List <string>(); void nonVanillaFileFoundCallback(string filepath) { Log.Error($@"Non-vanilla file found: {filepath}"); nonVanillaFiles.Add(filepath); } List <string> inconsistentDLC = new List <string>(); void inconsistentDLCFoundCallback(string filepath) { if (BackupSourceTarget.Supported) { Log.Error($@"DLC is in an inconsistent state: {filepath}"); inconsistentDLC.Add(filepath); } else { Log.Error(@"Detected an inconsistent DLC, likely due to an unofficial copy of the game"); } } ProgressVisible = true; ProgressIndeterminate = true; BackupStatus = M3L.GetString(M3L.string_validatingBackupSource); VanillaDatabaseService.LoadDatabaseFor(Game); bool isVanilla = VanillaDatabaseService.ValidateTargetAgainstVanilla(BackupSourceTarget, nonVanillaFileFoundCallback); bool isDLCConsistent = VanillaDatabaseService.ValidateTargetDLCConsistency(BackupSourceTarget, inconsistentDLCCallback: inconsistentDLCFoundCallback); List <string> dlcModsInstalled = VanillaDatabaseService.GetInstalledDLCMods(BackupSourceTarget); List <string> installedDLC = VanillaDatabaseService.GetInstalledOfficialDLC(BackupSourceTarget); List <string> allOfficialDLC = MEDirectories.OfficialDLC(BackupSourceTarget.Game); bool end = false; if (installedDLC.Count() < allOfficialDLC.Count()) { var dlcList = string.Join("\n - ", allOfficialDLC.Except(installedDLC).Select(x => $@"{MEDirectories.OfficialDLCNames(BackupSourceTarget.Game)[x]} ({x})")); //do not localize dlcList = @" - " + dlcList; Application.Current.Dispatcher.Invoke(delegate { var cancelDueToNotAllDLC = M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_notAllDLCInstalled, dlcList), M3L.GetString(M3L.string_someDlcNotInstalled), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (cancelDueToNotAllDLC == MessageBoxResult.No) { end = true; EndBackup(); return; } }); } if (!end) { if (isVanilla && isDLCConsistent && dlcModsInstalled.Count == 0) { BackupStatus = M3L.GetString(M3L.string_waitingForUserInput); string backupPath = null; Application.Current.Dispatcher.Invoke(delegate { Log.Information(@"Prompting user to select backup destination"); CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = M3L.GetString(M3L.string_selectBackupDestination) }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { backupPath = m.FileName; Log.Information(@"Backup path chosen: " + backupPath); //Check empty if (Directory.Exists(backupPath)) { if (Directory.GetFiles(backupPath).Length > 0 || Directory.GetDirectories(backupPath).Length > 0) { //Directory not empty Log.Error(@"Selected backup directory is not empty."); M3L.ShowDialog(window, M3L.GetString(M3L.string_directoryIsNotEmptyMustBeEmpty), M3L.GetString(M3L.string_directoryNotEmpty), MessageBoxButton.OK, MessageBoxImage.Error); end = true; EndBackup(); return; } } //Check space Utilities.GetDiskFreeSpaceEx(backupPath, out var freeBytes, out var totalBytes, out var totalFreeBytes); var requiredSpace = Utilities.GetSizeOfDirectory(BackupSourceTarget.TargetPath) * 1.1; //10% buffer if (freeBytes < requiredSpace) { //Not enough space. Log.Error( $@"Not enough disk spcae to create backup at {backupPath}. Required space: {ByteSize.FromBytes(requiredSpace)} Free space: {ByteSize.FromBytes(freeBytes)}"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogInsufficientDiskSpace, Path.GetPathRoot(backupPath), ByteSize.FromBytes(freeBytes).ToString(), ByteSize.FromBytes(requiredSpace).ToString()), M3L.GetString(M3L.string_insufficientDiskSpace), MessageBoxButton.OK, MessageBoxImage.Error); end = true; EndBackup(); return; } //Check it is not subdirectory of the game (we might want ot check its not subdir of a target) foreach (var target in AvailableBackupSources) { if (backupPath.IsSubPathOf(target.TargetPath)) { //Not enough space. Log.Error( $@"A backup cannot be created in a subdirectory of a game. {backupPath} is a subdir of {BackupSourceTarget.TargetPath}"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogBackupCannotBeSubdirectoryOfGame, backupPath, target.TargetPath), M3L.GetString(M3L.string_cannotCreateBackup), MessageBoxButton.OK, MessageBoxImage.Error); end = true; EndBackup(); return; } } } else { end = true; EndBackup(); return; } }); if (end) { return; } #region callbacks void fileCopiedCallback() { ProgressValue++; } string dlcFolderpath = MEDirectories.DLCPath(BackupSourceTarget) + '\\'; int dlcSubStringLen = dlcFolderpath.Length; bool aboutToCopyCallback(string file) { try { if (file.Contains(@"\cmmbackup\")) { return(false); //do not copy cmmbackup files } if (file.StartsWith(dlcFolderpath)) { //It's a DLC! string dlcname = file.Substring(dlcSubStringLen); dlcname = dlcname.Substring(0, dlcname.IndexOf('\\')); if (MEDirectories.OfficialDLCNames(BackupSourceTarget.Game) .TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, hrName); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, dlcname); } } else { //It's basegame if (file.EndsWith(@".bik")) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.GetString(M3L.string_movies)); } else if (new FileInfo(file).Length > 52428800) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, Path.GetFileName(file)); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_backingUpX, M3L.GetString(M3L.string_basegame)); } } } catch (Exception e) { Crashes.TrackError(e, new Dictionary <string, string>() { { @"dlcFolderpath", dlcFolderpath }, { @"dlcSubStringLen", dlcSubStringLen.ToString() }, { @"file", file } }); } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = M3L.GetString(M3L.string_creatingBackup); Log.Information($@"Backing up {BackupSourceTarget.TargetPath} to {backupPath}"); CopyDir.CopyAll_ProgressBar(new DirectoryInfo(BackupSourceTarget.TargetPath), new DirectoryInfo(backupPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { @"*.pdf", @"*.mp3" }); switch (Game) { case Mod.MEGame.ME1: case Mod.MEGame.ME2: Utilities.WriteRegistryKey(App.BACKUP_REGISTRY_KEY, Game + @"VanillaBackupLocation", backupPath); break; case Mod.MEGame.ME3: Utilities.WriteRegistryKey(App.REGISTRY_KEY_ME3CMM, @"VanillaCopyLocation", backupPath); break; } Log.Information($@"Writing cmm_vanilla"); File.Create(Path.Combine(backupPath, "cmm_vanilla")).Close(); Log.Information($@"Backup completed."); Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Success" } }); EndBackup(); return; }
private void BeginBackup() { if (Utilities.IsGameRunning(BackupSourceTarget.Game)) { Xceed.Wpf.Toolkit.MessageBox.Show(window, $"Cannot backup {Utilities.GetGameName(BackupSourceTarget.Game)} while it is running.", $"Game is running", MessageBoxButton.OK, MessageBoxImage.Error); return; } NamedBackgroundWorker bw = new NamedBackgroundWorker(Game.ToString() + "Backup"); bw.DoWork += (a, b) => { BackupInProgress = true; List <string> nonVanillaFiles = new List <string>(); void nonVanillaFileFoundCallback(string filepath) { Log.Error("Non-vanilla file found: " + filepath); nonVanillaFiles.Add(filepath); } List <string> inconsistentDLC = new List <string>(); void inconsistentDLCFoundCallback(string filepath) { if (BackupSourceTarget.Supported) { Log.Error("DLC is in an inconsistent state: " + filepath); inconsistentDLC.Add(filepath); } else { Log.Error("Detected an inconsistent DLC, likely due to an unofficial copy of the game"); } } ProgressVisible = true; ProgressIndeterminate = true; BackupStatus = "Validating backup source"; VanillaDatabaseService.LoadDatabaseFor(Game); bool isVanilla = VanillaDatabaseService.ValidateTargetAgainstVanilla(BackupSourceTarget, nonVanillaFileFoundCallback); bool isDLCConsistent = VanillaDatabaseService.ValidateTargetDLCConsistency(BackupSourceTarget, inconsistentDLCCallback: inconsistentDLCFoundCallback); List <string> dlcModsInstalled = VanillaDatabaseService.GetInstalledDLCMods(BackupSourceTarget); if (isVanilla && isDLCConsistent && dlcModsInstalled.Count == 0) { BackupStatus = "Waiting for user input"; string backupPath = null; bool end = false; Application.Current.Dispatcher.Invoke(delegate { CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = "Select backup destination" }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { //Check empty backupPath = m.FileName; if (Directory.Exists(backupPath)) { if (Directory.GetFiles(backupPath).Length > 0 || Directory.GetDirectories(backupPath).Length > 0) { //Directory not empty MessageBox.Show("Directory is not empty. A backup destination must be empty."); end = true; EndBackup(); return; } } } else { end = true; EndBackup(); return; } }); if (end) { return; } #region callbacks void fileCopiedCallback() { ProgressValue++; } string dlcFolderpath = MEDirectories.DLCPath(BackupSourceTarget) + '\\'; int dlcSubStringLen = dlcFolderpath.Length; bool aboutToCopyCallback(string file) { if (file.Contains("\\cmmbackup\\")) { return(false); //do not copy cmmbackup files } if (file.StartsWith(dlcFolderpath)) { //It's a DLC! string dlcname = file.Substring(dlcSubStringLen); dlcname = dlcname.Substring(0, dlcname.IndexOf('\\')); if (MEDirectories.OfficialDLCNames(BackupSourceTarget.Game).TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = "Backing up " + hrName; } else { BackupStatusLine2 = "Backing up " + dlcname; } } else { //It's basegame if (file.EndsWith(".bik")) { BackupStatusLine2 = "Backing up Movies"; } else if (new FileInfo(file).Length > 52428800) { BackupStatusLine2 = "Backing up " + Path.GetFileName(file); } else { BackupStatusLine2 = "Backing up BASEGAME"; } } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = "Creating backup"; CopyDir.CopyAll_ProgressBar(new DirectoryInfo(BackupSourceTarget.TargetPath), new DirectoryInfo(backupPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { "*.pdf", "*.mp3" }); switch (Game) { case Mod.MEGame.ME1: case Mod.MEGame.ME2: Utilities.WriteRegistryKey(App.BACKUP_REGISTRY_KEY, Game + "VanillaBackupLocation", backupPath); break; case Mod.MEGame.ME3: Utilities.WriteRegistryKey(App.REGISTRY_KEY_ME3CMM, "VanillaCopyLocation", backupPath); break; } Analytics.TrackEvent("Created a backup", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Success" } }); EndBackup(); return; } if (!isVanilla) { //Show UI for non vanilla Analytics.TrackEvent("Created a backup", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, Game modified" } }); b.Result = (nonVanillaFiles, "Cannot backup modified game", "The following files do not match the vanilla database and appear to be modified. Due to these files being modified, a backup cannot be taken of this installation."); } else if (!isDLCConsistent) { Analytics.TrackEvent("Created a backup", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, DLC inconsistent" } }); if (BackupSourceTarget.Supported) { b.Result = (inconsistentDLC, "Inconsistent DLC detected", "The following DLC are in an inconsistent state; they have a packed SFAR file but contain unpacked game files. The configuration of these DLC are not supported, the unpacked files must be manually removed."); } else { b.Result = ("Inconsistent DLC detected", "Inconsistent DLC was detected. This may be due to using an unofficial copy of the game."); } } else if (dlcModsInstalled.Count > 0) { Analytics.TrackEvent("Created a backup", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, DLC mods found" } }); b.Result = (dlcModsInstalled, "DLC mods are installed", "The following DLC folders were detected that are not part of the official game by BioWare. Backups cannot include DLC mods - the game must be unmodified."); } EndBackup(); }; bw.RunWorkerCompleted += (a, b) => { if (b.Result is (List <string> listItems, string title, string text)) { ListDialog ld = new ListDialog(listItems, title, text, window); ld.Show(); } else if (b.Result is (string errortitle, string message)) { Xceed.Wpf.Toolkit.MessageBox.Show(message, errortitle, MessageBoxButton.OK, MessageBoxImage.Error); } CommandManager.InvalidateRequerySuggested(); }; bw.RunWorkerAsync(); }
private void CheckModForTFCCompactability(DeploymentChecklistItem item) { // if (ModBeingDeployed.Game >= Mod.MEGame.ME2) //{ bool hasError = false; item.HasError = false; item.ItemText = M3L.GetString(M3L.string_checkingTexturesInMod); var referencedFiles = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList(); int numChecked = 0; GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game); var errors = new List <string>(); foreach (var f in referencedFiles) { if (_closed) { return; } numChecked++; item.ItemText = $@"{M3L.GetString(M3L.string_checkingTexturesInMod)} [{numChecked}/{referencedFiles.Count}]"; if (f.RepresentsPackageFilePath()) { Log.Information(@"Checking file for broken textures: " + f); var package = MEPackageHandler.OpenMEPackage(f); var textures = package.Exports.Where(x => x.IsTexture()).ToList(); foreach (var texture in textures) { if (_closed) { return; } var cache = texture.GetProperty <NameProperty>(@"TextureFileCacheName"); if (cache != null) { if (!VanillaDatabaseService.IsBasegameTFCName(cache.Value, ModBeingDeployed.Game)) { var mips = Texture2D.GetTexture2DMipInfos(texture, cache.Value); Texture2D tex = new Texture2D(texture); try { tex.GetImageBytesForMip(tex.GetTopMip(), validationTarget, false); //use active target } catch (Exception e) { Log.Information(@"Found broken texture: " + texture.GetInstancedFullPath); hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add(M3L.GetString(M3L.string_interp_couldNotLoadTextureData, texture.FileRef.FilePath, texture.GetInstancedFullPath, e.Message)); } } } } } } if (!hasError) { item.Foreground = Brushes.Green; item.Icon = FontAwesomeIcon.CheckCircle; item.ItemText = M3L.GetString(M3L.string_noBrokenTexturesWereFound); item.ToolTip = M3L.GetString(M3L.string_validationOK); } else { item.Errors = errors; item.ItemText = M3L.GetString(M3L.string_textureIssuesWereDetected); item.ToolTip = M3L.GetString(M3L.string_validationFailed); } item.HasError = hasError; }
private void CheckModForAFCCompactability(DeploymentChecklistItem item) { bool hasError = false; item.HasError = false; item.ItemText = M3L.GetString(M3L.string_checkingAudioReferencesInMod); var referencedFiles = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList(); int numChecked = 0; GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game); List <string> gameFiles = MEDirectories.EnumerateGameFiles(validationTarget.Game, validationTarget.TargetPath); var errors = new List <string>(); Dictionary <string, MemoryStream> cachedAudio = new Dictionary <string, MemoryStream>(); foreach (var f in referencedFiles) { if (_closed) { return; } numChecked++; item.ItemText = $@"{M3L.GetString(M3L.string_checkingAudioReferencesInMod)} [{numChecked}/{referencedFiles.Count}]"; if (f.RepresentsPackageFilePath()) { var package = MEPackageHandler.OpenMEPackage(f); var wwiseStreams = package.Exports.Where(x => x.ClassName == @"WwiseStream" && !x.IsDefaultObject).ToList(); foreach (var wwisestream in wwiseStreams) { if (_closed) { return; } //Check each reference. var afcNameProp = wwisestream.GetProperty <NameProperty>(@"Filename"); if (afcNameProp != null) { string afcNameWithExtension = afcNameProp + @".afc"; int audioSize = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 8); int audioOffset = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 4); string afcPath = null; Stream audioStream = null; var localDirectoryAFCPath = Path.Combine(Path.GetDirectoryName(wwisestream.FileRef.FilePath), afcNameWithExtension); bool isInOfficialArea = false; if (File.Exists(localDirectoryAFCPath)) { //local afc afcPath = localDirectoryAFCPath; } else { //Check game var fullPath = gameFiles.FirstOrDefault(x => Path.GetFileName(x).Equals(afcNameWithExtension, StringComparison.InvariantCultureIgnoreCase)); if (fullPath != null) { afcPath = fullPath; isInOfficialArea = MEDirectories.IsInBasegame(afcPath, validationTarget) || MEDirectories.IsInOfficialDLC(afcPath, validationTarget); } else if (cachedAudio.TryGetValue(afcNameProp.Value.Name, out var cachedAudioStream)) { audioStream = cachedAudioStream; //isInOfficialArea = true; //cached from vanilla SFAR } else if (MEDirectories.OfficialDLC(validationTarget.Game).Any(x => afcNameProp.Value.Name.StartsWith(x))) { var dlcName = afcNameProp.Value.Name.Substring(0, afcNameProp.Value.Name.LastIndexOf(@"_", StringComparison.InvariantCultureIgnoreCase)); var audio = VanillaDatabaseService.FetchFileFromVanillaSFAR(validationTarget, dlcName, afcNameWithExtension); if (audio != null) { cachedAudio[afcNameProp.Value.Name] = audio; } audioStream = audio; //isInOfficialArea = true; as this is in a vanilla SFAR we don't test against this since it will be correct. continue; } else { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add(M3L.GetString(M3L.string_interp_couldNotFindReferencedAFC, wwisestream.FileRef.FilePath, wwisestream.GetInstancedFullPath, afcNameProp.ToString())); continue; } } if (afcPath != null) { audioStream = new FileStream(afcPath, FileMode.Open); } try { audioStream.Seek(audioOffset, SeekOrigin.Begin); if (audioStream.ReadStringASCIINull(4) != @"RIFF") { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add(M3L.GetString(M3L.string_interp_invalidAudioPointer, wwisestream.FileRef.FilePath, wwisestream.GetInstancedFullPath)); if (audioStream is FileStream) { audioStream.Close(); } continue; } //attempt to seek audio length. audioStream.Seek(audioSize + 4, SeekOrigin.Current); //Check if this file is in basegame if (isInOfficialArea) { //Verify offset is not greater than vanilla size var vanillaInfo = VanillaDatabaseService.GetVanillaFileInfo(validationTarget, afcPath.Substring(validationTarget.TargetPath.Length + 1)); if (vanillaInfo == null) { Crashes.TrackError(new Exception($@"Vanilla information was null when performing vanilla file check for {afcPath.Substring(validationTarget.TargetPath.Length + 1)}")); } if (audioOffset >= vanillaInfo[0].size) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add(M3L.GetString(M3L.string_interp_audioStoredInOfficialAFC, wwisestream.FileRef.FilePath, wwisestream.GetInstancedFullPath)); } } if (audioStream is FileStream) { audioStream.Close(); } } catch (Exception e) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; if (audioStream is FileStream) { audioStream.Close(); } errors.Add(M3L.GetString(M3L.string_errorValidatingAudioReference, wwisestream.FileRef.FilePath, wwisestream.GetInstancedFullPath, e.Message)); continue; } } } } } if (!hasError) { item.Foreground = Brushes.Green; item.Icon = FontAwesomeIcon.CheckCircle; item.ItemText = M3L.GetString(M3L.string_noAudioIssuesWereDetected); item.ToolTip = M3L.GetString(M3L.string_validationOK); } else { item.Errors = errors; item.ItemText = M3L.GetString(M3L.string_audioIssuesWereDetected); item.ToolTip = M3L.GetString(M3L.string_validationFailed); } item.HasError = hasError; cachedAudio.Clear(); }
private Mod GenerateCompatibilityPackForFiles(List <string> dlcModList, List <string> filesToBePatched, SevenZipExtractor uiArchive) { dlcModList = dlcModList.Select(x => { var tpmi = ThirdPartyServices.GetThirdPartyModInfo(x, MEGame.ME3); if (tpmi == null) { return(x); } return(tpmi.modname); }).ToList(); string dlcs = string.Join("\n - ", dlcModList); // do not localize StarterKitOptions sko = new StarterKitOptions { ModName = M3L.GetString(M3L.string_guiCompatibilityPack), ModDescription = M3L.GetString(M3L.string_interp_compatPackModDescription, dlcs, DateTime.Now.ToString()), ModDeveloper = App.AppVersionHR, ModDLCFolderName = UI_MOD_NAME, ModGame = MEGame.ME3, ModInternalName = @"UI Mod Compatibility Pack", ModInternalTLKID = 1420400890, ModMountFlag = EMountFileFlag.ME3_SPOnly_NoSaveFileDependency, ModMountPriority = 31050, ModURL = null, ModModuleNumber = 0 }; Mod generatedMod = null; void modGenerated(Mod mod) { generatedMod = mod; lock (modGeneratedSignaler) { Monitor.Pulse(modGeneratedSignaler); } } void skUicallback(string text) { ActionSubstring = text; } StarterKitGeneratorWindow.CreateStarterKitMod(sko, skUicallback, modGenerated); lock (modGeneratedSignaler) { Monitor.Wait(modGeneratedSignaler); } //Mod has been generated. string outputPath = Path.Combine(generatedMod.ModPath, @"DLC_MOD_" + UI_MOD_NAME, @"CookedPCConsole"); ActionString = M3L.GetString(M3L.string_preparingUiLibrary); ActionSubstring = M3L.GetString(M3L.string_decompressingData); int done = 0; CaseInsensitiveDictionary <byte[]> uiLibraryData = new CaseInsensitiveDictionary <byte[]>(); var filesToDecompress = uiArchive.ArchiveFileData.Where(x => x.FileName.EndsWith(@".swf")).ToList(); foreach (var f in filesToDecompress) { MemoryStream decompressedStream = new MemoryStream(); uiArchive.ExtractFile(f.Index, decompressedStream); string fname = f.FileName.Substring(f.FileName.IndexOf('\\') + 1); fname = fname.Substring(0, fname.IndexOf(@".swf", StringComparison.InvariantCultureIgnoreCase)); uiLibraryData[fname] = decompressedStream.ToArray(); done++; Percent = getPercent(done, filesToDecompress.Count); } ActionString = M3L.GetString(M3L.string_patchingFiles); Percent = 0; done = 0; string singlesuffix = M3L.GetString(M3L.string_singularFile); string pluralsuffix = M3L.GetString(M3L.string_pluralFiles); // Logging Log.Information(@"The following files will be promoted in the compatibility pack:"); foreach (var file in filesToBePatched) { Log.Information(@" - " + file); } foreach (var file in filesToBePatched) { Log.Information(@"Patching file: " + file); ActionSubstring = Path.GetFileName(file); var package = MEPackageHandler.OpenMEPackage(file); if (package == null) { Log.Error(@"pPackage object is null!!!"); } var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList(); if (guiExports.Count > 0) { //potential item needing replacement //Check GUI library to see if we have anything. foreach (var export in guiExports) { if (uiLibraryData.TryGetValue(export.GetFullPath, out var newData)) { Log.Information(@" >> Patching export " + export.GetFullPath); //Patching this export. var exportProperties = export.GetProperties(); var rawData = exportProperties.GetProp <ArrayProperty <ByteProperty> >(@"RawData"); if (rawData == null) { Log.Error(@"Rawdata is null!!"); } rawData.Clear(); rawData.AddRange(newData.Select(x => new ByteProperty(x))); //This will be terribly slow. Need to port over new ME3Exp binary data handler export.WriteProperties(exportProperties); } else { Debug.WriteLine(@"Not patching gui export, file not in library: " + export.GetFullPath); } } var outpath = Path.Combine(outputPath, Path.GetFileName(package.FilePath)); if (package.IsModified) { Log.Information(@"Saving patched package to " + outpath); package.save(outpath, true); done++; ActionSubstring = M3L.GetString(M3L.string_interp_patchedXY, done.ToString(), done == 1 ? singlesuffix : pluralsuffix); } else { done++; Log.Information(@"File was patched but data did not change! " + outpath); } Percent = getPercent(done, filesToBePatched.Count); } else { throw new Exception($@"Error: {Path.GetFileName(file)} doesn't contain GUI exports! This shouldn't have been possible."); } } // Write builtagainst for metacmm. IniData iniData = new FileIniDataParser().ReadFile(generatedMod.ModDescPath); var builtAgainst = VanillaDatabaseService.GetInstalledDLCMods(target); builtAgainst.Remove(@"DLC_MOD_" + UI_MOD_NAME); iniData[@"ControllerCompat"][@"builtagainst"] = string.Join(';', builtAgainst); File.WriteAllText(generatedMod.ModDescPath, iniData.ToString()); return(generatedMod); }
private void CheckModForTFCCompactability(DeploymentChecklistItem item) { // if (ModBeingDeployed.Game >= Mod.MEGame.ME2) //{ bool hasError = false; item.HasError = false; item.ItemText = "Checking textures in mod"; var referencedFiles = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList(); int numChecked = 0; GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game); var errors = new List <string>(); foreach (var f in referencedFiles) { if (_closed) { return; } numChecked++; item.ItemText = $"Checking textures in mod [{numChecked}/{referencedFiles.Count}]"; if (f.RepresentsPackageFilePath()) { var package = MEPackageHandler.OpenMEPackage(f); var textures = package.Exports.Where(x => x.IsTexture()).ToList(); foreach (var texture in textures) { if (_closed) { return; } var cache = texture.GetProperty <NameProperty>("TextureFileCacheName"); if (cache != null) { if (!VanillaDatabaseService.IsBasegameTFCName(cache.Value, ModBeingDeployed.Game)) { var mips = Texture2D.GetTexture2DMipInfos(texture, cache.Value); Texture2D tex = new Texture2D(texture); try { tex.GetImageBytesForMip(tex.GetTopMip(), validationTarget, false); //use active target } catch (Exception e) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{texture.FileRef.FilePath} - {texture.GetInstancedFullPath}: Could not load texture data: {e.Message}"); } } } } } } if (!hasError) { item.Foreground = Brushes.Green; item.Icon = FontAwesomeIcon.CheckCircle; item.ItemText = "No broken textures were found"; item.ToolTip = "Validation passed"; } else { item.Errors = errors; item.ItemText = "Texture issues were detected"; item.ToolTip = "Validation failed"; } item.HasError = hasError; //} //} else //{ // item.Foreground = Brushes.Green; // item.Icon = FontAwesomeIcon.CheckCircle; // item.ItemText = "Textures are not "; // item.ToolTip = "Validation passed"; //} }
private void CompileIntoGame() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileIntoGameThread"); List <string> failedApplications = new List <string>(); nbw.DoWork += (a, b) => { BottomLeftMessage = M3L.GetString(M3L.string_compilingMixins); OperationInProgress = true; //DEBUG STUFF #if DEBUG int numCoresToApplyWith = 1; #else var numCoresToApplyWith = Environment.ProcessorCount; if (numCoresToApplyWith > 4) { numCoresToApplyWith = 4; //no more than 4 as this uses a lot of memory } #endif var mixins = AvailableOfficialMixins.Where(x => x.UISelectedForUse).ToList(); MixinHandler.LoadPatchDataForMixins(mixins); //before dynamic void failedApplicationCallback(string str) { failedApplications.Add(str); } var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback); if (failedApplications.Any()) { //Error building list Log.Information(@"Aborting mixin install due to incompatible selection of mixins"); return; } ProgressBarMax = mixins.Count(); ProgressBarValue = 0; int numdone = 0; void completedSingleApplicationCallback() { var val = Interlocked.Increment(ref numdone); ProgressBarValue = val; } //Mixins are ready to be applied Parallel.ForEach(compilingListsPerModule, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount > numCoresToApplyWith ? numCoresToApplyWith : Environment.ProcessorCount }, mapping => { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); //var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); //Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream, true); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackage(decompressedStream, $@"Vanilla - {Path.GetFileName(file.Key)}"); //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackage(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream var targetFile = Path.Combine(MEDirectories.CookedPath(SelectedInstallTarget), Path.GetFileName(file.Key)); var targetPackage = MEPackageHandler.OpenMEPackage(targetFile); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { targetPackage.save(); Log.Information("Three way merge succeeded for " + targetFile); } else { Log.Error("Could not merge three way merge into " + targetFile); } //var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); //package.save(outfile, false); // don't compress //finalStream.WriteToFile(outfile); //File.WriteAllBytes(outfile, finalStream.ToArray()); } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. var targetCookedPCDir = Path.Combine(MEDirectories.DLCPath(SelectedInstallTarget), dlcFolderName, @"CookedPCConsole"); var sfar = mapping.Key == ModJob.JobHeader.TESTPATCH ? Utilities.GetTestPatchPath(SelectedInstallTarget) : Path.Combine(targetCookedPCDir, @"Default.sfar"); bool unpacked = new FileInfo(sfar).Length == 32; DLCPackage targetDLCPackage = unpacked ? null : new DLCPackage(sfar); //cache SFAR target foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackage(decompressedStream, $@"VanillaDLC - {Path.GetFileName(file.Key)}"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackage(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream // must see if DLC is unpacked first MemoryStream targetFileStream = null; //Packed if (unpacked) { targetFileStream = new MemoryStream(File.ReadAllBytes(Path.Combine(targetCookedPCDir, file.Key))); } else { targetFileStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, Path.GetFileName(file.Key), forcedDLC: targetDLCPackage); } var targetPackage = MEPackageHandler.OpenMEPackage(targetFileStream, $@"Target package {dlcFolderName} - {file.Key}, from SFAR: {unpacked}"); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { if (unpacked) { targetPackage.save(); Log.Information("Three way merge succeeded for " + targetPackage.FilePath); } else { var finalSTream = targetPackage.saveToStream(); targetDLCPackage.ReplaceEntry(finalSTream.ToArray(), targetDLCPackage.FindFileEntry(Path.GetFileName(file.Key))); Log.Information("Three way merge succeeded for " + targetPackage.FileSourceForDebugging); } } else { Log.Error("Could not merge three way merge into " + targetFileStream); } } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }); MixinHandler.FreeME3TweaksPatchData(); var percent = 0; //this is used to save a localization BottomLeftMessage = $"Running AutoTOC on game {percent}%"; //Run autotoc void tocingUpdate(int percent) { BottomLeftMessage = $"Running AutoTOC on game {percent}%"; } AutoTOC.RunTOCOnGameTarget(SelectedInstallTarget, tocingUpdate); //Generate moddesc //IniData ini = new IniData(); //ini[@"ModManager"][@"cmmver"] = App.HighestSupportedModDesc.ToString(CultureInfo.InvariantCulture); //prevent commas //ini[@"ModInfo"][@"game"] = @"ME3"; //ini[@"ModInfo"][@"modname"] = modname; //ini[@"ModInfo"][@"moddev"] = App.AppVersionHR; //ini[@"ModInfo"][@"moddesc"] = M3L.GetString(M3L.string_compiledFromTheFollowingMixins); //ini[@"ModInfo"][@"modver"] = @"1.0"; //generateRepaceFilesMapping(ini, modpath); //File.WriteAllText(Path.Combine(modpath, @"moddesc.ini"), ini.ToString()); }; nbw.RunWorkerCompleted += (a, b) => { OperationInProgress = false; ClearMixinHandler(); if (failedApplications.Count > 0) { var ld = new ListDialog(failedApplications, M3L.GetString(M3L.string_failedToApplyAllMixins), M3L.GetString(M3L.string_theFollowingMixinsFailedToApply), mainwindow); ld.ShowDialog(); } /*if (modpath != null) * { * OnClosing(new DataEventArgs(modpath)); * } * else * {*/ BottomLeftMessage = "Mixins installed, maybe. Check logs"; //} }; CompilePanelButton.IsOpen = false; nbw.RunWorkerAsync(); }
private void CompileAsNewMod() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileAsNewModThread"); List <string> failedApplications = new List <string>(); var modname = NewModName; var modpath = Path.Combine(Utilities.GetME3ModsDirectory(), Utilities.SanitizePath(modname)); var result = M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_dialogCreatingNewModWithExistingName, NewModName, modpath), M3L.GetString(M3L.string_modAlreadyExists), MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); if (result == MessageBoxResult.No) { Log.Information(@"User has aborted mixin compilation due to same-named mod existing"); return; //abort. } nbw.DoWork += (a, b) => { BottomLeftMessage = M3L.GetString(M3L.string_compilingMixins); OperationInProgress = true; //DEBUG STUFF #if DEBUG int numCoresToApplyWith = 1; #else var numCoresToApplyWith = Environment.ProcessorCount; if (numCoresToApplyWith > 4) { numCoresToApplyWith = 4; //no more than 4 as this uses a lot of memory } #endif var mixins = AvailableOfficialMixins.Where(x => x.UISelectedForUse).ToList(); MixinHandler.LoadPatchDataForMixins(mixins); //before dynamic void failedApplicationCallback(string str) { failedApplications.Add(str); } var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback); if (failedApplications.Any()) { //Error building list modpath = null; Log.Information(@"Aborting mixin compiling due to incompatible selection of mixins"); return; } if (Directory.Exists(modpath)) { Utilities.DeleteFilesAndFoldersRecursively(modpath); } ProgressBarMax = mixins.Count(); ProgressBarValue = 0; int numdone = 0; void completedSingleApplicationCallback() { var val = Interlocked.Increment(ref numdone); ProgressBarValue = val; } //Mixins are ready to be applied Parallel.ForEach(compilingListsPerModule, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount > numCoresToApplyWith ? numCoresToApplyWith : Environment.ProcessorCount }, mapping => { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream, true); //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc"); using var finalStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackage(finalStream); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); package.save(outfile, false); // don't compress //finalStream.WriteToFile(outfile); //File.WriteAllBytes(outfile, finalStream.ToArray()); } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream); using var finalStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackage(finalStream); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); package.save(outfile, true); } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }); MixinHandler.FreeME3TweaksPatchData(); //Generate moddesc IniData ini = new IniData(); ini[@"ModManager"][@"cmmver"] = App.HighestSupportedModDesc.ToString(CultureInfo.InvariantCulture); //prevent commas ini[@"ModInfo"][@"game"] = @"ME3"; ini[@"ModInfo"][@"modname"] = modname; ini[@"ModInfo"][@"moddev"] = App.AppVersionHR; ini[@"ModInfo"][@"moddesc"] = M3L.GetString(M3L.string_compiledFromTheFollowingMixins); ini[@"ModInfo"][@"modver"] = @"1.0"; generateRepaceFilesMapping(ini, modpath); File.WriteAllText(Path.Combine(modpath, @"moddesc.ini"), ini.ToString()); }; nbw.RunWorkerCompleted += (a, b) => { OperationInProgress = false; ClearMixinHandler(); if (failedApplications.Count > 0) { var ld = new ListDialog(failedApplications, M3L.GetString(M3L.string_failedToApplyAllMixins), M3L.GetString(M3L.string_theFollowingMixinsFailedToApply), mainwindow); ld.ShowDialog(); } if (modpath != null) { OnClosing(new DataEventArgs(modpath)); } else { BottomLeftMessage = M3L.GetString(M3L.string_selectMixinsToCompile); } }; CompilePanelButton.IsOpen = false; nbw.RunWorkerAsync(); }
public static void ApplyMixinsToModule(KeyValuePair <ModJob.JobHeader, Dictionary <string, List <Mixin> > > mapping, string modpath, Action completedSingleApplicationCallback, Action <string> failedApplicationCallback) { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchBasegameFile(MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream, false, true); using var finalStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackageFromStream(finalStream); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); package.Save(outfile, false, includeAdditionalPackagesToCook: false, includeDependencyTable: true); // don't compress, use mixin saving rules for basegame files } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); //as file comes from backup, we don't need to decompress it, it will always be decompressed in sfar using var finalStream = MixinHandler.ApplyMixins(packageAsStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); if (mapping.Key != ModJob.JobHeader.TESTPATCH) { // TestPatch is never unpacked. So there is not really point to // compressing it's rather small files. The other DLC jobs likely will be packed still, but this will save some disk space. CLog.Information($@"Compressing package to mod directory: {outfile}", Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackageFromStream(finalStream); package.Save(outfile, true); } else { Log.Information($@"Writing patched file to disk: {outfile}"); finalStream.WriteToFile(outfile); } } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }
public void RestoreSFAR(bool batchRestore, Action signalRestoreCompleted = null) { bool?restore = batchRestore; if (!restore.Value) { restore = RestoreConfirmationCallback?.Invoke(FilePath); } if (restore.HasValue && restore.Value) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"RestoreSFARThread"); nbw.DoWork += (a, b) => { var backupFile = Path.Combine(BackupService.GetGameBackupPath(target.Game), FilePath); var targetFile = Path.Combine(target.TargetPath, FilePath); Restoring = true; var unpackedFiles = Directory.GetFiles(DLCDirectory, @"*", SearchOption.AllDirectories); RestoreButtonContent = M3L.GetString(M3L.string_cleaningUp); foreach (var file in unpackedFiles) { if (!file.EndsWith(@".sfar")) { Log.Information(@"Deleting unpacked file: " + file); File.Delete(file); } } // Check if we actually need to restore SFAR if (!VanillaDatabaseService.IsFileVanilla(target, targetFile, false)) { Log.Information($@"Restoring SFAR from backup: {backupFile} -> {targetFile}"); XCopy.Copy(backupFile, targetFile, true, true, (o, pce) => { RestoreButtonContent = M3L.GetString(M3L.string_interp_restoringXpercent, pce.ProgressPercentage.ToString()); }); } Utilities.DeleteEmptySubdirectories(DLCDirectory); RestoreButtonContent = M3L.GetString(M3L.string_restored); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occured in {nbw.Name} thread: {b.Error.Message}"); } //File.Copy(backupFile, targetFile, true); //if (!batchRestore) //{ RevalidateIsModified(); //restoreCompletedCallback?.Invoke(); //} Restoring = false; signalRestoreCompleted?.Invoke(); }; startingRestoreCallback?.Invoke(); nbw.RunWorkerAsync(); } }
public void ReloadGameTarget(bool lodUpdateAndLogging = true, bool forceLodUpdate = false) { if (Game != Mod.MEGame.Unknown && !IsCustomOption) { if (Directory.Exists(TargetPath)) { var oldTMOption = TextureModded; var alotInfo = GetInstalledALOTInfo(); if (alotInfo != null) { TextureModded = true; ALOTVersion = alotInfo.ToString(); if (alotInfo.MEUITMVER > 0) { MEUITMInstalled = true; MEUITMVersion = alotInfo.MEUITMVER; } } else { TextureModded = false; ALOTVersion = null; MEUITMInstalled = false; MEUITMVersion = 0; } CLog.Information(@"Getting game source for target " + TargetPath, lodUpdateAndLogging); var hashCheckResult = VanillaDatabaseService.GetGameSource(this); GameSource = hashCheckResult.result; ExecutableHash = hashCheckResult.hash; if (GameSource == null) { CLog.Error(@"Unknown source or illegitimate installation: " + hashCheckResult.hash, lodUpdateAndLogging); } else { if (GameSource.Contains(@"Origin") && Game == Mod.MEGame.ME3) { // Check for steam if (Directory.Exists(Path.Combine(TargetPath, @"__overlay"))) { GameSource += @" (Steam version)"; } } CLog.Information(@"Source: " + GameSource, lodUpdateAndLogging); } IsPolishME1 = Game == Mod.MEGame.ME1 && File.Exists(Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Movies", @"niebieska_pl.bik")); if (IsPolishME1) { CLog.Information(@"ME1 Polish Edition detected", lodUpdateAndLogging); } if (RegistryActive && (Settings.AutoUpdateLODs2K || Settings.AutoUpdateLODs4K) && oldTMOption != TextureModded && (lodUpdateAndLogging || forceLodUpdate)) { UpdateLODs(Settings.AutoUpdateLODs2K); } } else { Log.Error($@"Target is invalid: {TargetPath} does not exist (or is not accessible)"); IsValid = false; //set to false if target becomes invalid } } }