/// <summary>
        /// Starts preparing a mod by launching a background thread. This should be called from a UI thread
        /// </summary>
        private void StartPreparingModWrapper()
        {
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"UpdaterServiceUpload");

            nbw.WorkerReportsProgress = true;
            nbw.ProgressChanged      += (a, b) =>
            {
                if (b.UserState is double d)
                {
                    TaskbarHelper.SetProgress(d);
                }
                else if (b.UserState is TaskbarItemProgressState tbs)
                {
                    TaskbarHelper.SetProgressState(tbs);
                }
            };
            nbw.DoWork += (a, b) =>
            {
                OperationInProgress = true;
                b.Result            = UploadMod(d => nbw.ReportProgress(0, d), s => nbw.ReportProgress(0, s));
            };
            nbw.RunWorkerCompleted += (a, b) =>
            {
                if (b.Error != null)
                {
                    Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}");
                }
                TaskbarHelper.SetProgressState(TaskbarItemProgressState.None);
                Analytics.TrackEvent(@"Uploaded mod to updater service", new Dictionary <string, string>()
                {
                    { @"Result", b.Result?.ToString() },
                    { @"Mod", mod.ModName + @" " + mod.ModVersionString }
                });
                OperationInProgress = false;
            };
            nbw.RunWorkerAsync();
        }
示例#2
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()
            {
                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)
                    {
                        TaskbarHelper.SetProgress(d);
                    }
                    else if (b.UserState is TaskbarProgressBarState tbs)
                    {
                        TaskbarHelper.SetProgressState(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();
                    var installedDLC   = VanillaDatabaseService.GetInstalledOfficialDLC(targetToBackup);
                    var 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: ");
                        Log.Information(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;
                            }
                        });
                    }

                    Log.Information(@"Checking for TexturesMEM TFCs");
                    var memTextures = Directory.GetFiles(targetToBackup.TargetPath, @"TexturesMEM*.tfc", SearchOption.AllDirectories);

                    if (end)
                    {
                        return;
                    }
                    if (isVanilla && isDLCConsistent && !Enumerable.Any(dlcModsInstalled) && !Enumerable.Any(memTextures))
                    {
                        BackupStatus = M3L.GetString(M3L.string_waitingForUserInput);

                        string backupPath = null;
                        if (!targetToBackup.IsCustomOption)
                        {
                            // Creating a new backup
                            nbw.ReportProgress(0, TaskbarProgressBarState.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, TaskbarProgressBarState.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   = M3Directories.GetDLCPath(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, TaskbarProgressBarState.Normal);
                            }

                            BackupStatus = M3L.GetString(M3L.string_creatingBackup);
                            Log.Information($@"Backing up {targetToBackup.TargetPath} to {backupPath}");
                            nbw.ReportProgress(0, TaskbarProgressBarState.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 MEGame.ME1:
                        case MEGame.ME2:
                            Utilities.WriteRegistryKey(App.BACKUP_REGISTRY_KEY, Game + @"VanillaBackupLocation",
                                                       backupPath);
                            break;

                        case 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 (Enumerable.Any(dlcModsInstalled))
                    {
                        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));
                    }
                    else if (Enumerable.Any(memTextures))
                    {
                        Analytics.TrackEvent(@"Created a backup", new Dictionary <string, string>()
                        {
                            { @"Game", Game.ToString() },
                            { @"Result", @"Failure, TexturesMEM files found" }
                        });
                        b.Result = (M3L.GetString(M3L.string_leftoverTextureFilesFound),
                                    M3L.GetString(M3L.string_dialog_foundLeftoverTextureFiles));
                    }
                    EndBackup();
                };
                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 (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();
            }
示例#4
0
 private void Timer_OnProgress(object sender, int e)
 {
     Threading.ComponentInvoke(progressBar1, (x) => x.Value = e);
     TaskbarHelper.SetProgress(SaiHelper.AppProcess, e, progressBar1.Maximum);
 }
示例#5
0
        public static async void StartRestore(MainWindow mw, bool isQuick, Action postRestoreDelegate = null)
        {
            var pd = await mw.ShowProgressAsync("Restoring game", "Preparing to restore game");

            pd.SetIndeterminate();
            await Task.Run(() =>
            {
                if (isQuick)
                {
                    // Nuke the DLC
                    MERLog.Information(@"Quick restore started");
                    pd.SetMessage("Removing randomize DLC component");
                    var dlcModPath = MERFileSystem.GetDLCModPath();
                    if (Directory.Exists(dlcModPath))
                    {
                        MERLog.Information($@"Deleting {dlcModPath}");
                        Utilities.DeleteFilesAndFoldersRecursively(dlcModPath);
                    }

                    mw.DLCComponentInstalled = false;


                    // Restore basegame only files
                    pd.SetMessage("Restoring randomized basegame files");
                    var isControllerModInstalled = SFXGame.IsControllerBasedInstall();

                    var backupPath       = BackupService.GetGameBackupPath(MERFileSystem.Game, out _, false);
                    var gameCookedPath   = M3Directories.GetCookedPath(Locations.GetTarget(MERFileSystem.Game));
                    var backupCookedPath = MEDirectories.GetCookedPath(MERFileSystem.Game, backupPath);
                    foreach (var bgf in MERFileSystem.alwaysBasegameFiles)
                    {
                        var srcPath  = Path.Combine(backupCookedPath, bgf);
                        var destPath = Path.Combine(gameCookedPath, bgf);
                        MERLog.Information($@"Restoring {bgf}");
                        File.Copy(srcPath, destPath, true);
                    }

                    if (isControllerModInstalled)
                    {
                        // We must also restore Coalesced.ini or it will reference a UI that is no longer available and game will not boot
                        MERLog.Information(@"Controller based install detected, also restoring Coalesced.ini to prevent startup crash");
                        File.Copy(Path.Combine(backupPath, "BioGame", "Config", "PC", "Cooked", "Coalesced.ini"), Path.Combine(Locations.GetTarget(MERFileSystem.Game).TargetPath, "BioGame", "Config", "PC", "Cooked", "Coalesced.ini"), true);
                    }

                    // Delete basegame TFC
                    var baseTFC = MERFileSystem.GetTFCPath(false);
                    if (File.Exists(baseTFC))
                    {
                        File.Delete(baseTFC);
                    }

                    // Done!
                }
                else
                {
                    // Full restore
                    var target = Locations.GetTarget(MERFileSystem.Game);
                    MERLog.Information($@"Performing full game restore on {target.TargetPath} target after restore");

                    object syncObj = new object();
                    BackupHandler.GameRestore gr = new BackupHandler.GameRestore(MERFileSystem.Game)
                    {
                        ConfirmationCallback = (title, message) =>
                        {
                            bool response = false;
                            Application.Current.Dispatcher.Invoke(async() =>
                            {
                                response = await mw.ShowMessageAsync(title, message,
                                                                     MessageDialogStyle.AffirmativeAndNegative,
                                                                     new MetroDialogSettings()
                                {
                                    AffirmativeButtonText = "OK",
                                    NegativeButtonText    = "Cancel",
                                }) == MessageDialogResult.Affirmative;
                                lock (syncObj)
                                {
                                    Monitor.Pulse(syncObj);
                                }
                            });
                            lock (syncObj)
                            {
                                Monitor.Wait(syncObj);
                            }

                            return(response);
                        },
                        BlockingErrorCallback = (title, message) => { Application.Current.Dispatcher.Invoke(async() => { await mw.ShowMessageAsync(title, message); }); },
                        RestoreErrorCallback  = (title, message) => { Application.Current.Dispatcher.Invoke(async() => { await mw.ShowMessageAsync(title, message); }); },
                        UpdateStatusCallback  = message =>
                                                Application.Current.Dispatcher.Invoke(() => pd.SetMessage(message)),
                        UpdateProgressCallback = (done, total) =>
                                                 Application.Current.Dispatcher.Invoke(() =>
                        {
                            pd.SetProgress(done * 1d / total);
                            if (total != 0)
                            {
                                TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal);
                                TaskbarHelper.SetProgress(done * 1.0 / total);
                            }
                        }),
                        SetProgressIndeterminateCallback = indeterminate => Application.Current.Dispatcher.Invoke(() =>
                        {
                            if (indeterminate)
                            {
                                pd.SetIndeterminate();
                            }
                            TaskbarHelper.SetProgressState(indeterminate ? TaskbarProgressBarState.Indeterminate : TaskbarProgressBarState.Normal);
                        }),
                        SelectDestinationDirectoryCallback = (title, message) =>
                        {
                            string selectedPath = null;
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                // Not sure if this has to be synced
                                CommonOpenFileDialog ofd = new CommonOpenFileDialog()
                                {
                                    Title            = "Select restore destination directory",
                                    IsFolderPicker   = true,
                                    EnsurePathExists = true
                                };
                                if (ofd.ShowDialog() == CommonFileDialogResult.Ok)
                                {
                                    selectedPath = ofd.FileName;
                                }
                            });
                            return(selectedPath);
                        }
                    };
                    gr.PerformRestore(target.TargetPath);
                    mw.DLCComponentInstalled = false;
                    MERLog.Information(@"Reloading target after restore");
                    target.ReloadGameTarget(false, false);
                    mw.SetupTargetDescriptionText();
                }
            }).ContinueWithOnUIThread(async x =>
            {
                TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress);
                await pd.CloseAsync();
                postRestoreDelegate?.Invoke();
            });
        }
            private void BeginRestore()
            {
                if (Utilities.IsGameRunning(Game))
                {
                    M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialogCannotRestoreXWhileItIsRunning, Game.ToGameName()), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                var useNewMethod = M3L.ShowDialog(window,
                                                  M3L.GetString(M3L.string_beta_useNewRestoreMethod),
                                                  M3L.GetString(M3L.string_useBetaFeatureQuestion), MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;

                bool restore = RestoreTarget.IsCustomOption || useNewMethod; //custom option is restore to custom location

                restore = restore || M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_restoringXWillDeleteGameDir, Game.ToGameName()), M3L.GetString(M3L.string_gameTargetWillBeDeleted), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes;
                if (restore)
                {
                    NamedBackgroundWorker nbw = new NamedBackgroundWorker(Game + @"-Restore");
                    nbw.WorkerReportsProgress = true;
                    nbw.ProgressChanged      += (a, b) =>
                    {
                        if (b.UserState is double d)
                        {
                            TaskbarHelper.SetProgress(d);
                        }
                    };
                    nbw.DoWork += (a, b) =>
                    {
                        RestoreInProgress = true;
                        // Nuke the LODs
                        if (!RestoreTarget.IsCustomOption && RestoreTarget.Game.IsOTGame())
                        {
                            Log.Information($@"Resetting LODs for {RestoreTarget.Game}");
                            Utilities.SetLODs(RestoreTarget, false, false, false);
                        }

                        string restoreTargetPath = b.Argument as string;
                        string backupPath        = BackupLocation;

                        if (!useNewMethod)
                        {
                            BackupStatusLine2 = M3L.GetString(M3L.string_deletingExistingGameInstallation);
                            if (Directory.Exists(restoreTargetPath))
                            {
                                if (Directory.GetFiles(restoreTargetPath).Any() ||
                                    Directory.GetDirectories(restoreTargetPath).Any())
                                {
                                    Log.Information(@"Deleting existing game directory: " + restoreTargetPath);
                                    try
                                    {
                                        bool deletedDirectory =
                                            Utilities.DeleteFilesAndFoldersRecursively(restoreTargetPath);
                                        if (deletedDirectory != true)
                                        {
                                            b.Result = RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY;
                                            return;
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        //todo: handle this better
                                        Log.Error(
                                            $@"Exception deleting game directory: {restoreTargetPath}: {ex.Message}");
                                        b.Result = RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY;
                                        return;
                                    }
                                }
                            }
                            else
                            {
                                Log.Error(@"Game directory not found! Was it removed while the app was running?");
                            }

                            var created = Utilities.CreateDirectoryWithWritePermission(restoreTargetPath);
                            if (!created)
                            {
                                b.Result = RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY;
                                return;
                            }
                        }

                        BackupStatusLine2 = M3L.GetString(M3L.string_restoringGameFromBackup);
                        if (restoreTargetPath != null)
                        {
                            //callbacks

                            #region callbacks

                            void fileCopiedCallback()
                            {
                                ProgressValue++;
                                if (ProgressMax != 0)
                                {
                                    nbw.ReportProgress(0, ProgressValue * 1.0 / ProgressMax);
                                }
                            }

                            string dlcFolderpath   = MEDirectories.GetDLCPath(Game, backupPath) + '\\'; //\ at end makes sure we are restoring a subdir
                            int    dlcSubStringLen = dlcFolderpath.Length;
                            Debug.WriteLine(@"DLC Folder: " + dlcFolderpath);
                            Debug.Write(@"DLC Folder path len:" + dlcFolderpath);

                            bool aboutToCopyCallback(string fileBeingCopied)
                            {
                                if (fileBeingCopied.Contains(@"\cmmbackup\"))
                                {
                                    return(false);                                          //do not copy cmmbackup files
                                }
                                Debug.WriteLine(fileBeingCopied);
                                if (fileBeingCopied.StartsWith(dlcFolderpath, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    //It's a DLC!
                                    string dlcname = fileBeingCopied.Substring(dlcSubStringLen);
                                    int    index   = dlcname.IndexOf('\\');
                                    if (index > 0) //Files directly in the DLC directory won't have path sep
                                    {
                                        try
                                        {
                                            dlcname = dlcname.Substring(0, index);
                                            if (MEDirectories.OfficialDLCNames(RestoreTarget.Game).TryGetValue(dlcname, out var hrName))
                                            {
                                                BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, hrName);
                                            }
                                            else
                                            {
                                                BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, dlcname);
                                            }
                                        }
                                        catch (Exception e)
                                        {
                                            Crashes.TrackError(e, new Dictionary <string, string>()
                                            {
                                                { @"Source", @"Restore UI display callback" },
                                                { @"Value", fileBeingCopied },
                                                { @"DLC Folder path", dlcFolderpath }
                                            });
                                        }
                                    }
                                }
                                else
                                {
                                    //It's basegame
                                    if (fileBeingCopied.EndsWith(@".bik"))
                                    {
                                        BackupStatusLine2 = M3L.GetString(M3L.string_restoringMovies);
                                    }
                                    else if (new FileInfo(fileBeingCopied).Length > 52428800)
                                    {
                                        BackupStatusLine2 = M3L.GetString(M3L.string_interp_restoringX, Path.GetFileName(fileBeingCopied));
                                    }
                                    else
                                    {
                                        BackupStatusLine2 = M3L.GetString(M3L.string_restoringBasegame);
                                    }
                                }

                                return(true);
                            }

                            void totalFilesToCopyCallback(int total)
                            {
                                ProgressValue         = 0;
                                ProgressIndeterminate = false;
                                ProgressMax           = total;
                            }

                            #endregion

                            BackupStatus = M3L.GetString(M3L.string_restoringGame);

                            // LE: Backup Config file so settings don't get lost
                            string configText = null;
                            var    configPath = RestoreTarget.Game.IsLEGame() ? M3Directories.GetLODConfigFile(RestoreTarget) : null;
                            if (File.Exists(configPath))
                            {
                                configText = File.ReadAllText(configPath); // backup to memory
                            }

                            Log.Information($@"Copying backup to game directory: {backupPath} -> {restoreTargetPath}");
                            if (useNewMethod)
                            {
                                string      CurrentRCFile = null;
                                RoboCommand rc            = new RoboCommand();
                                rc.CopyOptions.Destination = restoreTargetPath;
                                rc.CopyOptions.Source      = backupPath;
                                rc.CopyOptions.Mirror      = true;
                                rc.CopyOptions.MultiThreadedCopiesCount = 2;
                                rc.OnCopyProgressChanged += (sender, args) =>
                                {
                                    ProgressIndeterminate = false;
                                    ProgressValue         = (int)args.CurrentFileProgress;
                                    ProgressMax           = 100;
                                };
                                rc.OnFileProcessed += (sender, args) =>
                                {
                                    if (args.ProcessedFile.Name.StartsWith(backupPath) && args.ProcessedFile.Name.Length > backupPath.Length)
                                    {
                                        CurrentRCFile     = args.ProcessedFile.Name.Substring(backupPath.Length + 1);
                                        BackupStatusLine2 = M3L.GetString(M3L.string_interp_copyingX, CurrentRCFile);
                                    }
                                };
                                rc.Start().Wait();
                            }
                            else
                            {
                                CopyDir.CopyAll_ProgressBar(new DirectoryInfo(backupPath),
                                                            new DirectoryInfo(restoreTargetPath),
                                                            totalItemsToCopyCallback: totalFilesToCopyCallback,
                                                            aboutToCopyCallback: aboutToCopyCallback,
                                                            fileCopiedCallback: fileCopiedCallback,
                                                            ignoredExtensions: new[] { @"*.pdf", @"*.mp3" });
                            }

                            Log.Information(@"Restore of game data has completed");

                            if (configText != null)
                            {
                                // Restore config file
                                try
                                {
                                    Directory.CreateDirectory(Directory.GetParent(configPath).FullName);
                                    File.WriteAllText(configPath, configText);
                                    Log.Information(@"Restored config file");
                                }
                                catch (Exception e)
                                {
                                    Log.Error($@"Could not restore config file: {e.Message}");
                                }
                            }
                            BackupCopyFinished(restoreTargetPath);
                        }
                    };
                    nbw.RunWorkerCompleted += (a, b) =>
                    {
                        if (b.Error != null)
                        {
                            Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}");
                        }
                        TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress);
                        if (b.Result is RestoreResult result)
                        {
                            switch (result)
                            {
                            case RestoreResult.ERROR_COULD_NOT_CREATE_DIRECTORY:
                                Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>()
                                {
                                    { @"Game", Game.ToString() },
                                    { @"Result", @"Failure, Could not create target directory" }
                                });
                                M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCouldNotCreateGameDirectoryAfterDeletion), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error);
                                break;

                            case RestoreResult.ERROR_COULD_NOT_DELETE_GAME_DIRECTORY:
                                Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>()
                                {
                                    { @"Game", Game.ToString() },
                                    { @"Result", @"Failure, Could not delete existing game directory" }
                                });
                                M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogcouldNotFullyDeleteGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error);
                                break;

                            case RestoreResult.EXCEPTION_DELETING_GAME_DIRECTORY:
                                Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>()
                                {
                                    { @"Game", Game.ToString() },
                                    { @"Result", @"Failure, Exception deleting existing game directory" }
                                });
                                M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogErrorOccuredDeletingGameDirectory), M3L.GetString(M3L.string_errorRestoringGame), MessageBoxButton.OK, MessageBoxImage.Error);
                                break;

                            case RestoreResult.RESTORE_OK:
                                Analytics.TrackEvent(@"Restored game", new Dictionary <string, string>()
                                {
                                    { @"Game", Game.ToString() },
                                    { @"Result", @"Success" }
                                });
                                break;
                            }
                        }

                        EndRestore();
                        CommandManager.InvalidateRequerySuggested();
                    };
                    var restoreTargetPath = RestoreTarget.TargetPath;
                    if (RestoreTarget.IsCustomOption)
                    {
                        CommonOpenFileDialog m = new CommonOpenFileDialog
                        {
                            IsFolderPicker   = true,
                            EnsurePathExists = true,
                            Title            = M3L.GetString(M3L.string_selectNewRestoreDestination)
                        };
                        if (m.ShowDialog() == CommonFileDialogResult.Ok)
                        {
                            //Check empty
                            restoreTargetPath = m.FileName;
                            if (Directory.Exists(restoreTargetPath))
                            {
                                if (Directory.GetFiles(restoreTargetPath).Length > 0 || Directory.GetDirectories(restoreTargetPath).Length > 0)
                                {
                                    Log.Warning($@"The selected restore directory is not empty: {restoreTargetPath}");
                                    //Directory not empty
                                    if (!useNewMethod)
                                    {
                                        M3L.ShowDialog(window,
                                                       M3L.GetString(M3L
                                                                     .string_dialogDirectoryIsNotEmptyLocationToRestoreToMustBeEmpty),
                                                       M3L.GetString(M3L.string_cannotRestoreToThisLocation), MessageBoxButton.OK,
                                                       MessageBoxImage.Error);
                                        return;
                                    }
                                    else
                                    {
                                        // Warn user
                                        var shouldContinue = MessageBoxResult.Yes == M3L.ShowDialog(window,
                                                                                                    M3L.GetString(M3L.string_interp_directoryNotEmptyWillDeleteEverything, restoreTargetPath),
                                                                                                    M3L.GetString(M3L.string_directoryNotEmpty), MessageBoxButton.YesNo, MessageBoxImage.Warning);
                                        if (!shouldContinue)
                                        {
                                            return;
                                        }
                                        Log.Warning($@"The user is continuing to new-gen restore on existing directory anyways");
                                    }
                                }

                                //TODO: PREVENT RESTORING TO DOCUMENTS/BIOWARE
                            }

                            Analytics.TrackEvent(@"Chose to restore game to custom location", new Dictionary <string, string>()
                            {
                                { @"Game", Game.ToString() },
                                { @"New-gen", useNewMethod.ToString() }
                            });
                        }
                        else
                        {
                            return;
                        }
                    }

                    RefreshTargets = true;
                    TaskbarHelper.SetProgress(0);
                    TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal);
                    nbw.RunWorkerAsync(restoreTargetPath);
                }
            }
        private void UpdateModMakerMod(OnlineContent.ModMakerModUpdateInfo mui)
        {
            //throw new NotImplementedException();
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"ModmakerModUpdaterThread-" + mui.mod.ModName);

            nbw.WorkerReportsProgress = true;
            nbw.ProgressChanged      += (a, b) =>
            {
                if (b.UserState is double d)
                {
                    TaskbarHelper.SetProgress(d);
                }
            };
            nbw.DoWork += (a, b) =>
            {
                mui.DownloadButtonText = M3L.GetString(M3L.string_compiling);

                OperationInProgress  = true;
                mui.UpdateInProgress = true;
                mui.Indeterminate    = false;

                mui.UIStatusString = M3L.GetString(M3L.string_downloadingDelta);
                var normalEndpoint = OnlineContent.ModmakerModsEndpoint + mui.ModMakerId;
                var lzmaEndpoint   = normalEndpoint + @"&method=lzma";

                string modDelta = null;

                //Try LZMA first
                try
                {
                    var download = OnlineContent.DownloadToMemory(lzmaEndpoint);
                    if (download.errorMessage == null)
                    {
                        mui.UIStatusString = M3L.GetString(M3L.string_decompressingDelta);
                        // OK
                        var decompressed = SevenZipHelper.LZMA.DecompressLZMAFile(download.result.ToArray());
                        modDelta = Encoding.UTF8.GetString(decompressed);
                    }
                    else
                    {
                        Log.Error(@"Error downloading lzma mod delta to memory: " + download.errorMessage);
                    }
                }
                catch (Exception e)
                {
                    Log.Error(@"Error downloading LZMA mod delta to memory: " + e.Message);
                }

                if (modDelta == null)
                {
                    //failed to download LZMA.
                    var download = OnlineContent.DownloadToMemory(normalEndpoint);
                    if (download.errorMessage == null)
                    {
                        //OK
                        modDelta = Encoding.UTF8.GetString(download.result.ToArray());
                    }
                    else
                    {
                        Log.Error(@"Error downloading decompressed mod delta to memory: " + download.errorMessage);
                    }
                }

                void setOverallMax(int max)
                {
                    mui.OverallProgressMax = max;
                }

                void setOverallValue(int current)
                {
                    mui.OverallProgressValue = current;
                    nbw.ReportProgress(0, current * 1.0 / mui.OverallProgressMax);
                    if (current > mui.OverallProgressMax)
                    {
                        Debugger.Break();
                    }
                }

                void setCurrentTaskString(string str)
                {
                    mui.UIStatusString = str;
                }

                if (modDelta != null)
                {
                    var compiler = new ModMakerCompiler(mui.ModMakerId);
                    //compiler.SetCurrentMaxCallback = SetCurrentMax;
                    //compiler.SetCurrentValueCallback = SetCurrentProgressValue;
                    compiler.SetOverallMaxCallback   = setOverallMax;
                    compiler.SetOverallValueCallback = setOverallValue;
                    //compiler.SetCurrentTaskIndeterminateCallback = SetCurrentTaskIndeterminate;
                    compiler.SetCurrentTaskStringCallback = setCurrentTaskString;
                    //compiler.SetModNameCallback = SetModNameOrDownloadText;
                    //compiler.SetCompileStarted = CompilationInProgress;
                    //compiler.SetModNotFoundCallback = ModNotFound;
                    Mod m = compiler.DownloadAndCompileMod(modDelta);
                    if (m != null)
                    {
                        try
                        {
                            File.WriteAllText(System.IO.Path.Combine(Utilities.GetModmakerDefinitionsCache(), mui.ModMakerId + @".xml"), modDelta);
                        }
                        catch (Exception e)
                        {
                            Log.Error(@"Couldn't cache modmaker xml file: " + e.Message);
                        }

                        mui.DownloadButtonText = M3L.GetString(M3L.string_updated);
                        mui.UIStatusString     = M3L.GetString(M3L.string_interp_modMakerCodeX, mui.ModMakerId);
                        mui.UpdateInProgress   = false;
                        mui.CanUpdate          = false;
                        AnyModUpdated          = true;
                    }
                    else
                    {
                        mui.UpdateInProgress   = false;
                        mui.DownloadButtonText = M3L.GetString(M3L.string_compilingFailed);
                        mui.UpdateInProgress   = false;
                    }
                }
            };
            nbw.RunWorkerCompleted += (a, b) =>
            {
                if (b.Error != null)
                {
                    Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}");
                }
                Analytics.TrackEvent(@"Updated mod", new Dictionary <string, string>()
                {
                    { @"Type", @"ModMaker" },
                    { @"ModName", mui.mod.ModName },
                    { @"Result", mui.CanUpdate ? @"Success" : @"Failed" }
                });

                TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress);
                OperationInProgress = false;
                CommandManager.InvalidateRequerySuggested();
            };
            TaskbarHelper.SetProgress(0);
            TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal);
            nbw.RunWorkerAsync();
        }
        private void UpdateClassicMod(OnlineContent.ModUpdateInfo ui)
        {
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"ModUpdaterThread-" + ui.mod.ModName);

            nbw.WorkerReportsProgress = true;
            nbw.ProgressChanged      += (a, b) =>
            {
                if (b.UserState is double d)
                {
                    TaskbarHelper.SetProgress(d);
                }
            };
            nbw.DoWork += (a, b) =>
            {
                OperationInProgress   = true;
                ui.UpdateInProgress   = true;
                ui.Indeterminate      = false;
                ui.DownloadButtonText = M3L.GetString(M3L.string_downloading);
                ui.ProgressChanged   += (a, b) =>
                {
                    if (b.totalToDl != 0 && nbw.IsBusy) //? IsBusy needs to be here for some reason or it crashes, like progress comes in late or something.
                    {
                        nbw.ReportProgress(0, b.currentDl * 1.0 / b.totalToDl);
                    }
                };
                bool errorShown = false;
                void errorCallback(string message)
                {
                    if (!errorShown)
                    {
                        errorShown = true;
                        Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_errorOccuredWhileUpdatingXErrorMessage, ui.mod.ModName, message), M3L.GetString(M3L.string_interp_errorUpdatingX, ui.mod.ModName), MessageBoxButton.OK, MessageBoxImage.Error); }
                                                              );
                    }
                }

                var stagingDirectory = Directory.CreateDirectory(Path.Combine(Utilities.GetTempPath(), Path.GetFileName(ui.mod.ModPath))).FullName;
                var modUpdated       = OnlineContent.UpdateMod(ui, stagingDirectory, errorCallback);
                ui.UpdateInProgress   = false;
                ui.CanUpdate          = !modUpdated;
                AnyModUpdated        |= modUpdated;
                ui.DownloadButtonText = ui.CanUpdate ? M3L.GetString(M3L.string_downloadUpdate) : M3L.GetString(M3L.string_updated);
                Utilities.DeleteFilesAndFoldersRecursively(stagingDirectory);
            };
            nbw.RunWorkerCompleted += (a, b) =>
            {
                if (b.Error != null)
                {
                    Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}");
                }
                Analytics.TrackEvent(@"Updated mod", new Dictionary <string, string>()
                {
                    { @"Type", @"Classic" },
                    { @"ModName", ui.mod.ModName },
                    { @"Result", ui.CanUpdate ? @"Success" : @"Failed" }
                });
                TaskbarHelper.SetProgressState(TaskbarProgressBarState.NoProgress);
                OperationInProgress = false;
                CommandManager.InvalidateRequerySuggested();
            };
            TaskbarHelper.SetProgress(0);
            TaskbarHelper.SetProgressState(TaskbarProgressBarState.Normal);
            nbw.RunWorkerAsync();
        }