/// <summary> /// Starts preparing a mod by launching a background thread. This should be called from a UI thread /// </summary> private void StartPreparingModWrapper() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"UpdaterServiceUpload"); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { TaskbarHelper.SetProgress(d); } else if (b.UserState is TaskbarItemProgressState tbs) { TaskbarHelper.SetProgressState(tbs); } }; nbw.DoWork += (a, b) => { OperationInProgress = true; b.Result = UploadMod(d => nbw.ReportProgress(0, d), s => nbw.ReportProgress(0, s)); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } TaskbarHelper.SetProgressState(TaskbarItemProgressState.None); Analytics.TrackEvent(@"Uploaded mod to updater service", new Dictionary <string, string>() { { @"Result", b.Result?.ToString() }, { @"Mod", mod.ModName + @" " + mod.ModVersionString } }); OperationInProgress = false; }; nbw.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 UpdateModMakerMod(OnlineContent.ModMakerModUpdateInfo mui) { //throw new NotImplementedException(); NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"ModmakerModUpdaterThread-" + mui.mod.ModName); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { mainwindow.TaskBarItemInfoHandler.ProgressValue = d; } }; nbw.DoWork += (a, b) => { mui.DownloadButtonText = M3L.GetString(M3L.string_compiling); OperationInProgress = true; mui.UpdateInProgress = true; mui.Indeterminate = false; mui.UIStatusString = M3L.GetString(M3L.string_downloadingDelta); var normalEndpoint = OnlineContent.ModmakerModsEndpoint + mui.ModMakerId; var lzmaEndpoint = normalEndpoint + @"&method=lzma"; string modDelta = null; //Try LZMA first try { var download = OnlineContent.DownloadToMemory(lzmaEndpoint); if (download.errorMessage == null) { mui.UIStatusString = M3L.GetString(M3L.string_decompressingDelta); // OK var decompressed = SevenZipHelper.LZMA.DecompressLZMAFile(download.result.ToArray()); modDelta = Encoding.UTF8.GetString(decompressed); } else { Log.Error(@"Error downloading lzma mod delta to memory: " + download.errorMessage); } } catch (Exception e) { Log.Error(@"Error downloading LZMA mod delta to memory: " + e.Message); } if (modDelta == null) { //failed to download LZMA. var download = OnlineContent.DownloadToMemory(normalEndpoint); if (download.errorMessage == null) { //OK modDelta = Encoding.UTF8.GetString(download.result.ToArray()); } else { Log.Error(@"Error downloading decompressed mod delta to memory: " + download.errorMessage); } } void setOverallMax(int max) { mui.OverallProgressMax = max; } void setOverallValue(int current) { mui.OverallProgressValue = current; nbw.ReportProgress(0, current * 1.0 / mui.OverallProgressMax); if (current > mui.OverallProgressMax) { Debugger.Break(); } } void setCurrentTaskString(string str) { mui.UIStatusString = str; } if (modDelta != null) { var compiler = new ModMakerCompiler(mui.ModMakerId); //compiler.SetCurrentMaxCallback = SetCurrentMax; //compiler.SetCurrentValueCallback = SetCurrentProgressValue; compiler.SetOverallMaxCallback = setOverallMax; compiler.SetOverallValueCallback = setOverallValue; //compiler.SetCurrentTaskIndeterminateCallback = SetCurrentTaskIndeterminate; compiler.SetCurrentTaskStringCallback = setCurrentTaskString; //compiler.SetModNameCallback = SetModNameOrDownloadText; //compiler.SetCompileStarted = CompilationInProgress; //compiler.SetModNotFoundCallback = ModNotFound; Mod m = compiler.DownloadAndCompileMod(modDelta); if (m != null) { try { File.WriteAllText(System.IO.Path.Combine(Utilities.GetModmakerDefinitionsCache(), mui.ModMakerId + @".xml"), modDelta); } catch (Exception e) { Log.Error(@"Couldn't cache modmaker xml file: " + e.Message); } mui.DownloadButtonText = M3L.GetString(M3L.string_updated); mui.UIStatusString = M3L.GetString(M3L.string_interp_modMakerCodeX, mui.ModMakerId); mui.UpdateInProgress = false; mui.CanUpdate = false; AnyModUpdated = true; } else { mui.UpdateInProgress = false; mui.DownloadButtonText = M3L.GetString(M3L.string_compilingFailed); mui.UpdateInProgress = false; } } }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occured in {nbw.Name} thread: {b.Error.Message}"); } Analytics.TrackEvent(@"Updated mod", new Dictionary <string, string>() { { @"Type", @"ModMaker" }, { @"ModName", mui.mod.ModName }, { @"Result", mui.CanUpdate ? @"Success" : @"Failed" } }); mainwindow.TaskBarItemInfoHandler.ProgressState = TaskbarItemProgressState.None; OperationInProgress = false; CommandManager.InvalidateRequerySuggested(); }; mainwindow.TaskBarItemInfoHandler.ProgressValue = 0; mainwindow.TaskBarItemInfoHandler.ProgressState = TaskbarItemProgressState.Normal; nbw.RunWorkerAsync(); }
private void UpdateClassicMod(OnlineContent.ModUpdateInfo ui) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"ModUpdaterThread-" + ui.mod.ModName); nbw.WorkerReportsProgress = true; nbw.ProgressChanged += (a, b) => { if (b.UserState is double d) { mainwindow.TaskBarItemInfoHandler.ProgressValue = d; } }; nbw.DoWork += (a, b) => { OperationInProgress = true; ui.UpdateInProgress = true; ui.Indeterminate = false; ui.DownloadButtonText = M3L.GetString(M3L.string_downloading); ui.ProgressChanged += (a, b) => { if (b.totalToDl != 0 && nbw.IsBusy) //? IsBusy needs to be here for some reason or it crashes, like progress comes in late or something. { nbw.ReportProgress(0, b.currentDl * 1.0 / b.totalToDl); } }; bool errorShown = false; void errorCallback(string message) { if (!errorShown) { errorShown = true; Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_errorOccuredWhileUpdatingXErrorMessage, ui.mod.ModName, message), M3L.GetString(M3L.string_interp_errorUpdatingX, ui.mod.ModName), MessageBoxButton.OK, MessageBoxImage.Error); } ); } } var stagingDirectory = Directory.CreateDirectory(Path.Combine(Utilities.GetTempPath(), Path.GetFileName(ui.mod.ModPath))).FullName; var modUpdated = OnlineContent.UpdateMod(ui, stagingDirectory, errorCallback); ui.UpdateInProgress = false; ui.CanUpdate = !modUpdated; AnyModUpdated |= modUpdated; ui.DownloadButtonText = ui.CanUpdate ? M3L.GetString(M3L.string_downloadUpdate) : M3L.GetString(M3L.string_updated); Utilities.DeleteFilesAndFoldersRecursively(stagingDirectory); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occured in {nbw.Name} thread: {b.Error.Message}"); } Analytics.TrackEvent(@"Updated mod", new Dictionary <string, string>() { { @"Type", @"Classic" }, { @"ModName", ui.mod.ModName }, { @"Result", ui.CanUpdate ? @"Success" : @"Failed" } }); mainwindow.TaskBarItemInfoHandler.ProgressState = TaskbarItemProgressState.None; OperationInProgress = false; CommandManager.InvalidateRequerySuggested(); }; mainwindow.TaskBarItemInfoHandler.ProgressValue = 0; mainwindow.TaskBarItemInfoHandler.ProgressState = TaskbarItemProgressState.Normal; nbw.RunWorkerAsync(); }
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 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); } }