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); 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 BeginRestore() { if (Utilities.IsGameRunning(Game)) { Xceed.Wpf.Toolkit.MessageBox.Show(window, $"Cannot restore {Utilities.GetGameName(Game)} while it is running.", $"Game is running", MessageBoxButton.OK, MessageBoxImage.Error); return; } NamedBackgroundWorker bw = new NamedBackgroundWorker(Game.ToString() + "Backup"); bw.DoWork += (a, b) => { RestoreInProgress = true; string restoreTargetPath = b.Argument as string; string backupPath = BackupLocation; BackupStatusLine2 = "Deleting existing game installation"; if (Directory.Exists(restoreTargetPath)) { if (Directory.GetFiles(restoreTargetPath).Any() || Directory.GetDirectories(restoreTargetPath).Any()) { Log.Information("Deleting existing game directory: " + restoreTargetPath); try { bool deletedDirectory = Utilities.DeleteFilesAndFoldersRecursively(restoreTargetPath); if (deletedDirectory != true) { b.Result = RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY; return; } } catch (Exception ex) { //todo: handle this better Log.Error("Exception deleting game directory: " + restoreTargetPath + ": " + ex.Message); b.Result = RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY; return; } } } else { Log.Error("Game directory not found! Was it removed while the app was running?"); } //Todo: Revert LODs, remove IndirectSound settings (MEUITM) //Log.Information("Reverting lod settings"); //string exe = BINARY_DIRECTORY + MEM_EXE_NAME; //string args = "--remove-lods --gameid " + BACKUP_THREAD_GAME; //Utilities.runProcess(exe, args); //if (BACKUP_THREAD_GAME == 1) //{ // string iniPath = IniSettingsHandler.GetConfigIniPath(1); // if (File.Exists(iniPath)) // { // Log.Information("Reverting Indirect Sound ini fix for ME1"); // IniFile engineConf = new IniFile(iniPath); // engineConf.DeleteKey("DeviceName", "ISACTAudio.ISACTAudioDevice"); // } //} var created = Utilities.CreateDirectoryWithWritePermission(restoreTargetPath); if (!created) { b.Result = RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY; return; } BackupStatusLine2 = "Restoring game from backup"; if (restoreTargetPath != null) { //callbacks #region callbacks void fileCopiedCallback() { ProgressValue++; } string dlcFolderpath = MEDirectories.DLCPath(backupPath, Game) + '\\'; //\ at end makes sure we are restoring a subdir int dlcSubStringLen = dlcFolderpath.Length; Debug.WriteLine("DLC Folder: " + dlcFolderpath); Debug.Write("DLC Fodler path len:" + dlcFolderpath); bool aboutToCopyCallback(string fileBeingCopied) { if (fileBeingCopied.Contains("\\cmmbackup\\")) { return(false); //do not copy cmmbackup files } Debug.WriteLine(fileBeingCopied); if (fileBeingCopied.StartsWith(dlcFolderpath, StringComparison.InvariantCultureIgnoreCase)) { //It's a DLC! string dlcname = fileBeingCopied.Substring(dlcSubStringLen); int index = dlcname.IndexOf('\\'); try { dlcname = dlcname.Substring(0, index); if (MEDirectories.OfficialDLCNames(RestoreTarget.Game).TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = "Restoring " + hrName; } else { BackupStatusLine2 = "Restoring " + dlcname; } } catch (Exception e) { Crashes.TrackError(e, new Dictionary <string, string>() { { "Source", "Restore UI display callback" }, { "Value", fileBeingCopied }, { "DLC Folder path", dlcFolderpath } }); } } else { //It's basegame if (fileBeingCopied.EndsWith(".bik")) { BackupStatusLine2 = "Restoring Movies"; } else if (new FileInfo(fileBeingCopied).Length > 52428800) { BackupStatusLine2 = "Restoring " + Path.GetFileName(fileBeingCopied); } else { BackupStatusLine2 = "Restoring BASEGAME"; } } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = "Restoring game"; Log.Information("Copying backup to game directory: " + backupPath + " -> " + restoreTargetPath); CopyDir.CopyAll_ProgressBar(new DirectoryInfo(backupPath), new DirectoryInfo(restoreTargetPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { "*.pdf", "*.mp3" }); Log.Information("Restore of game data has completed"); } //Check for cmmvanilla file and remove it present string cmmVanilla = Path.Combine(restoreTargetPath, "cmm_vanilla"); if (File.Exists(cmmVanilla)) { Log.Information("Removing cmm_vanilla file"); File.Delete(cmmVanilla); } Log.Information("Restore thread wrapping up"); b.Result = RestoreResult.RESTORE_OK; }; bw.RunWorkerCompleted += (a, b) => { if (b.Result is RestoreResult result) { switch (result) { case RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY: Analytics.TrackEvent("Restored game", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, Could not create target directory" } }); Xceed.Wpf.Toolkit.MessageBox.Show("Could not create the game directory after it was deleted due to an error. View the logs from the help menu for more information.", "Error restoring game", MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY: Analytics.TrackEvent("Restored game", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, Could not delete existing game directory" } }); Xceed.Wpf.Toolkit.MessageBox.Show("Could not fully delete the game directory. It may have files or folders still open from various programs. Part of the game may have been deleted. View the logs from the help menu for more information.", "Error restoring game", MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY: Analytics.TrackEvent("Restored game", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Failure, Excpetion deleting existing game directory" } }); Xceed.Wpf.Toolkit.MessageBox.Show("An error occured while deleting the game directory. View the logs from the help menu for more information.", "Error restoring game", MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.RESTORE_OK: Analytics.TrackEvent("Restored game", new Dictionary <string, string>() { { "Game", Game.ToString() }, { "Result", "Success" } }); break; } } EndRestore(); CommandManager.InvalidateRequerySuggested(); }; var restTarget = RestoreTarget.TargetPath; if (RestoreTarget.IsCustomOption) { CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = "Select new restore destination" }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { //Check empty restTarget = m.FileName; if (Directory.Exists(restTarget)) { if (Directory.GetFiles(restTarget).Length > 0 || Directory.GetDirectories(restTarget).Length > 0) { //Directory not empty Xceed.Wpf.Toolkit.MessageBox.Show("Directory is not empty. Location to restore to must be empty.", "Cannot restore to this location", MessageBoxButton.OK, MessageBoxImage.Error); return; } } Analytics.TrackEvent("Chose to restore game to custom location", new Dictionary <string, string>() { { "Game", Game.ToString() } }); } else { return; } } RefreshTargets = true; bw.RunWorkerAsync(restTarget); }
private void BeginRestore() { if (Utilities.IsGameRunning(Game)) { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialogCannotRestoreXWhileItIsRunning, Utilities.GetGameName(Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return; } bool restore = RestoreTarget.IsCustomOption; //custom option is restore to custom location restore = restore || M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_restoringXWillDeleteGameDir, Utilities.GetGameName(Game)), M3L.GetString(M3L.string_gameTargetWillBeDeleted), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes; if (restore) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(Game + @"-Restore"); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { TaskbarHelper.SetProgress(d); } }; nbw.DoWork += (a, b) => { RestoreInProgress = true; string restoreTargetPath = b.Argument as string; string backupPath = BackupLocation; BackupStatusLine2 = M3L.GetString(M3L.string_deletingExistingGameInstallation); if (Directory.Exists(restoreTargetPath)) { if (Directory.GetFiles(restoreTargetPath).Any() || Directory.GetDirectories(restoreTargetPath).Any()) { Log.Information(@"Deleting existing game directory: " + restoreTargetPath); try { bool deletedDirectory = Utilities.DeleteFilesAndFoldersRecursively(restoreTargetPath); if (deletedDirectory != true) { b.Result = RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY; return; } } catch (Exception ex) { //todo: handle this better Log.Error($@"Exception deleting game directory: {restoreTargetPath}: {ex.Message}"); b.Result = RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY; return; } } } else { Log.Error(@"Game directory not found! Was it removed while the app was running?"); } //Todo: Revert LODs, remove IndirectSound settings (MEUITM) var created = Utilities.CreateDirectoryWithWritePermission(restoreTargetPath); if (!created) { b.Result = RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY; return; } BackupStatusLine2 = M3L.GetString(M3L.string_restoringGameFromBackup); if (restoreTargetPath != null) { //callbacks #region callbacks void fileCopiedCallback() { ProgressValue++; if (ProgressMax != 0) { nbw.ReportProgress(0, ProgressValue * 1.0 / ProgressMax); } } string dlcFolderpath = MEDirectories.DLCPath(backupPath, Game) + '\\'; //\ at end makes sure we are restoring a subdir int dlcSubStringLen = dlcFolderpath.Length; Debug.WriteLine(@"DLC Folder: " + dlcFolderpath); Debug.Write(@"DLC Fodler path len:" + dlcFolderpath); bool aboutToCopyCallback(string fileBeingCopied) { if (fileBeingCopied.Contains(@"\cmmbackup\")) { return(false); //do not copy cmmbackup files } Debug.WriteLine(fileBeingCopied); if (fileBeingCopied.StartsWith(dlcFolderpath, StringComparison.InvariantCultureIgnoreCase)) { //It's a DLC! string dlcname = fileBeingCopied.Substring(dlcSubStringLen); int index = dlcname.IndexOf('\\'); try { dlcname = dlcname.Substring(0, index); if (MEDirectories.OfficialDLCNames(RestoreTarget.Game).TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, hrName); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, dlcname); } } catch (Exception e) { Crashes.TrackError(e, new Dictionary <string, string>() { { @"Source", @"Restore UI display callback" }, { @"Value", fileBeingCopied }, { @"DLC Folder path", dlcFolderpath } }); } } else { //It's basegame if (fileBeingCopied.EndsWith(@".bik")) { BackupStatusLine2 = M3L.GetString(M3L.string_restoringMovies); } else if (new FileInfo(fileBeingCopied).Length > 52428800) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, Path.GetFileName(fileBeingCopied)); } else { BackupStatusLine2 = M3L.GetString(M3L.string_restoringBasegame); } } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = M3L.GetString(M3L.string_restoringGame); Log.Information($@"Copying backup to game directory: {backupPath} -> {restoreTargetPath}"); CopyDir.CopyAll_ProgressBar(new DirectoryInfo(backupPath), new DirectoryInfo(restoreTargetPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { @"*.pdf", @"*.mp3" }); Log.Information(@"Restore of game data has completed"); } //Check for cmmvanilla file and remove it present string cmmVanilla = Path.Combine(restoreTargetPath, @"cmm_vanilla"); if (File.Exists(cmmVanilla)) { Log.Information(@"Removing cmm_vanilla file"); File.Delete(cmmVanilla); } Log.Information(@"Restore thread wrapping up"); RestoreTarget.ReloadGameTarget(); b.Result = RestoreResult.RESTORE_OK; }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress); if (b.Result is RestoreResult result) { switch (result) { case RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Could not create target directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCouldNotCreateGameDirectoryAfterDeletion), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Could not delete existing game directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogcouldNotFullyDeleteGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Exception deleting existing game directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogErrorOccuredDeletingGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.RESTORE_OK: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Success" } }); break; } } EndRestore(); CommandManager.InvalidateRequerySuggested(); }; var restTarget = RestoreTarget.TargetPath; if (RestoreTarget.IsCustomOption) { CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = M3L.GetString(M3L.string_selectNewRestoreDestination) }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { //Check empty restTarget = m.FileName; if (Directory.Exists(restTarget)) { if (Directory.GetFiles(restTarget).Length > 0 || Directory.GetDirectories(restTarget).Length > 0) { //Directory not empty M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogDirectoryIsNotEmptyLocationToRestoreToMustBeEmpty), M3L.GetString(M3L.string_cannotRestoreToThisLocation), MessageBoxButton.OK, MessageBoxImage.Error); return; } //TODO: PREVENT RESTORING TO DOCUMENTS/BIOWARE } Analytics.TrackEvent(@"Chose to restore game to custom location", new Dictionary <string, string>() { { @"Game", Game.ToString() } }); } else { return; } } RefreshTargets = true; TaskbarHelper.SetProgress(0); TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal); nbw.RunWorkerAsync(restTarget); } }
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 BeginRestore() { if (Utilities.IsGameRunning(Game)) { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialogCannotRestoreXWhileItIsRunning, Game.ToGameName()), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return; } var useNewMethod = M3L.ShowDialog(window, M3L.GetString(M3L.string_beta_useNewRestoreMethod), M3L.GetString(M3L.string_useBetaFeatureQuestion), MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes; bool restore = RestoreTarget.IsCustomOption || useNewMethod; //custom option is restore to custom location restore = restore || M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_restoringXWillDeleteGameDir, Game.ToGameName()), M3L.GetString(M3L.string_gameTargetWillBeDeleted), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes; if (restore) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(Game + @"-Restore"); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { TaskbarHelper.SetProgress(d); } }; nbw.DoWork += (a, b) => { RestoreInProgress = true; // Nuke the LODs if (!RestoreTarget.IsCustomOption && RestoreTarget.Game.IsOTGame()) { Log.Information($@"Resetting LODs for {RestoreTarget.Game}"); Utilities.SetLODs(RestoreTarget, false, false, false); } string restoreTargetPath = b.Argument as string; string backupPath = BackupLocation; if (!useNewMethod) { BackupStatusLine2 = M3L.GetString(M3L.string_deletingExistingGameInstallation); if (Directory.Exists(restoreTargetPath)) { if (Directory.GetFiles(restoreTargetPath).Any() || Directory.GetDirectories(restoreTargetPath).Any()) { Log.Information(@"Deleting existing game directory: " + restoreTargetPath); try { bool deletedDirectory = Utilities.DeleteFilesAndFoldersRecursively(restoreTargetPath); if (deletedDirectory != true) { b.Result = RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY; return; } } catch (Exception ex) { //todo: handle this better Log.Error( $@"Exception deleting game directory: {restoreTargetPath}: {ex.Message}"); b.Result = RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY; return; } } } else { Log.Error(@"Game directory not found! Was it removed while the app was running?"); } var created = Utilities.CreateDirectoryWithWritePermission(restoreTargetPath); if (!created) { b.Result = RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY; return; } } BackupStatusLine2 = M3L.GetString(M3L.string_restoringGameFromBackup); if (restoreTargetPath != null) { //callbacks #region callbacks void fileCopiedCallback() { ProgressValue++; if (ProgressMax != 0) { nbw.ReportProgress(0, ProgressValue * 1.0 / ProgressMax); } } string dlcFolderpath = MEDirectories.GetDLCPath(Game, backupPath) + '\\'; //\ at end makes sure we are restoring a subdir int dlcSubStringLen = dlcFolderpath.Length; Debug.WriteLine(@"DLC Folder: " + dlcFolderpath); Debug.Write(@"DLC Folder path len:" + dlcFolderpath); bool aboutToCopyCallback(string fileBeingCopied) { if (fileBeingCopied.Contains(@"\cmmbackup\")) { return(false); //do not copy cmmbackup files } Debug.WriteLine(fileBeingCopied); if (fileBeingCopied.StartsWith(dlcFolderpath, StringComparison.InvariantCultureIgnoreCase)) { //It's a DLC! string dlcname = fileBeingCopied.Substring(dlcSubStringLen); int index = dlcname.IndexOf('\\'); if (index > 0) //Files directly in the DLC directory won't have path sep { try { dlcname = dlcname.Substring(0, index); if (MEDirectories.OfficialDLCNames(RestoreTarget.Game).TryGetValue(dlcname, out var hrName)) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, hrName); } else { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, dlcname); } } catch (Exception e) { Crashes.TrackError(e, new Dictionary <string, string>() { { @"Source", @"Restore UI display callback" }, { @"Value", fileBeingCopied }, { @"DLC Folder path", dlcFolderpath } }); } } } else { //It's basegame if (fileBeingCopied.EndsWith(@".bik")) { BackupStatusLine2 = M3L.GetString(M3L.string_restoringMovies); } else if (new FileInfo(fileBeingCopied).Length > 52428800) { BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, Path.GetFileName(fileBeingCopied)); } else { BackupStatusLine2 = M3L.GetString(M3L.string_restoringBasegame); } } return(true); } void totalFilesToCopyCallback(int total) { ProgressValue = 0; ProgressIndeterminate = false; ProgressMax = total; } #endregion BackupStatus = M3L.GetString(M3L.string_restoringGame); // LE: Backup Config file so settings don't get lost string configText = null; var configPath = RestoreTarget.Game.IsLEGame() ? M3Directories.GetLODConfigFile(RestoreTarget) : null; if (File.Exists(configPath)) { configText = File.ReadAllText(configPath); // backup to memory } Log.Information($@"Copying backup to game directory: {backupPath} -> {restoreTargetPath}"); if (useNewMethod) { string CurrentRCFile = null; RoboCommand rc = new RoboCommand(); rc.CopyOptions.Destination = restoreTargetPath; rc.CopyOptions.Source = backupPath; rc.CopyOptions.Mirror = true; rc.CopyOptions.MultiThreadedCopiesCount = 2; rc.OnCopyProgressChanged += (sender, args) => { ProgressIndeterminate = false; ProgressValue = (int)args.CurrentFileProgress; ProgressMax = 100; }; rc.OnFileProcessed += (sender, args) => { if (args.ProcessedFile.Name.StartsWith(backupPath) && args.ProcessedFile.Name.Length > backupPath.Length) { CurrentRCFile = args.ProcessedFile.Name.Substring(backupPath.Length + 1); BackupStatusLine2 = M3L.GetString(M3L.string_interp_copyingX, CurrentRCFile); } }; rc.Start().Wait(); } else { CopyDir.CopyAll_ProgressBar(new DirectoryInfo(backupPath), new DirectoryInfo(restoreTargetPath), totalItemsToCopyCallback: totalFilesToCopyCallback, aboutToCopyCallback: aboutToCopyCallback, fileCopiedCallback: fileCopiedCallback, ignoredExtensions: new[] { @"*.pdf", @"*.mp3" }); } Log.Information(@"Restore of game data has completed"); if (configText != null) { // Restore config file try { Directory.CreateDirectory(Directory.GetParent(configPath).FullName); File.WriteAllText(configPath, configText); Log.Information(@"Restored config file"); } catch (Exception e) { Log.Error($@"Could not restore config file: {e.Message}"); } } BackupCopyFinished(restoreTargetPath); } }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress); if (b.Result is RestoreResult result) { switch (result) { case RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Could not create target directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCouldNotCreateGameDirectoryAfterDeletion), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Could not delete existing game directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogcouldNotFullyDeleteGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Failure, Exception deleting existing game directory" } }); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogErrorOccuredDeletingGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error); break; case RestoreResult.RESTORE_OK: Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"Result", @"Success" } }); break; } } EndRestore(); CommandManager.InvalidateRequerySuggested(); }; var restoreTargetPath = RestoreTarget.TargetPath; if (RestoreTarget.IsCustomOption) { CommonOpenFileDialog m = new CommonOpenFileDialog { IsFolderPicker = true, EnsurePathExists = true, Title = M3L.GetString(M3L.string_selectNewRestoreDestination) }; if (m.ShowDialog() == CommonFileDialogResult.Ok) { //Check empty restoreTargetPath = m.FileName; if (Directory.Exists(restoreTargetPath)) { if (Directory.GetFiles(restoreTargetPath).Length > 0 || Directory.GetDirectories(restoreTargetPath).Length > 0) { Log.Warning($@"The selected restore directory is not empty: {restoreTargetPath}"); //Directory not empty if (!useNewMethod) { M3L.ShowDialog(window, M3L.GetString(M3L .string_dialogDirectoryIsNotEmptyLocationToRestoreToMustBeEmpty), M3L.GetString(M3L.string_cannotRestoreToThisLocation), MessageBoxButton.OK, MessageBoxImage.Error); return; } else { // Warn user var shouldContinue = MessageBoxResult.Yes == M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_directoryNotEmptyWillDeleteEverything, restoreTargetPath), M3L.GetString(M3L.string_directoryNotEmpty), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (!shouldContinue) { return; } Log.Warning($@"The user is continuing to new-gen restore on existing directory anyways"); } } //TODO: PREVENT RESTORING TO DOCUMENTS/BIOWARE } Analytics.TrackEvent(@"Chose to restore game to custom location", new Dictionary <string, string>() { { @"Game", Game.ToString() }, { @"New-gen", useNewMethod.ToString() } }); } else { return; } } RefreshTargets = true; TaskbarHelper.SetProgress(0); TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal); nbw.RunWorkerAsync(restoreTargetPath); } }
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; }