예제 #1
0
            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();
            }
예제 #3
0
            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);
            }
예제 #4
0
            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);
                }
            }
예제 #7
0
            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;
                        }