public async Task <XmlHistory> PatchAsync(LaunchType launchType)
        {
            var history = new XmlHistory();

            history.Success = false;

            var totalProgress = new ProgressObject();

            var logForm = new LogForm(totalProgress);

            logForm.InvokeIfRequired(() => logForm.Show());

            try
            {
                List <PatchGroup> patches = PatchManager.SaveInstructions(ref history);

                await Task.Run(() => PatchManager.ApplyInstructions(launchType, patches, totalProgress)).ConfigureAwait(false);

                history.Success = true;
            }
            catch (Exception exception)
            {
                Logger.Error(exception, "Patching failed because instructions cannot be applied.");
            }

            if (!history.Success)
            {
                PatchingHelper.RestorePatchedFiles(AppContextManager.Context.Value, history.Files);
            }

            logForm.InvokeIfRequired(() => CloseForm(logForm));

            return(history);
        }
        public async Task LaunchTestRunAsync(LaunchType launchType = LaunchType.Test)
        {
            StartUnlockDialog();

            SetState(LaunchManagerState.IsPatching);

            XmlHistory history = await this.PatchAsync(launchType).ConfigureAwait(false);

            SetState(LaunchManagerState.Idle);

            PatchingHelper.RestorePatchedFiles(AppContextManager.Context.Value, history.Files);

            if (!PreferencesManager.OpenLogAfterPatch)
            {
                return;
            }

            try
            {
                string logPath = LogManager.Logs.First(kvp => kvp.Key == "PatchManager").Value;

                TryOpenTextFile(logPath);
            }
            catch (Exception exception)
            {
                Logger.Error(exception, "Cannot open log file after test run");
            }
        }
Exemple #3
0
        public async Task LaunchTestRunAsync(LaunchType launchType = LaunchType.Test)
        {
            MainWindow.Enabled = false;
            new Thread(o => AskUnlockGUI()).Start();

            State.Value = LaunchManagerState.IsPatching;

            XmlHistory history = await this.PatchAsync(launchType).ConfigureAwait(false);

            State.Value = LaunchManagerState.Idle;

            PatchingHelper.RestorePatchedFiles(AppContextManager.Context.Value, history.Files);

            if (PreferencesManager.OpenLogAfterPatch)
            {
                try
                {
                    string logPath = LogManager.Logs.First(kvp => kvp.Key == "PatchManager").Value;

                    TryOpenTextFile(logPath);
                }
                catch (Exception exception)
                {
                    Logger.Error(exception, "Cannot open log file after test run");
                }
            }
        }
        public static void RestorePatchedFiles()
        {
            if (History != null)
            {
                Logger.Debug("Trying to restore patched files because we have History...");

                PatchingHelper.RestorePatchedFiles(AppContextManager.Context.Value, History.Files);
            }
        }
Exemple #5
0
        public async Task <XmlHistory> Command_Patch()
        {
            State.Value = LaunchManagerState.IsPatching;
            var history = new XmlHistory
            {
                Success = false
            };

            try
            {
                var progObj = new ProgressObject();
                using (var logForm = new LogForm(progObj)
                {
                    Icon = _home.Icon
                })
                {
                    logForm.Show();
                    try
                    {
                        var patches = GroupPatches(Instructions).ToList();
                        history.Files = patches.Select(XmlFileHistory.FromInstrGroup).ToList();
                        _historySerializer.Serialize(history, _pathHistoryXml);
                        await Task.Run(() => ApplyInstructions(patches, progObj));

                        history.Success = true;
                    }
                    catch (PatchingProcessException ex)
                    {
                        Command_Display_Patching_Error(ex);
                    }
                    if (!history.Success)
                    {
                        PatchingHelper.RestorePatchedFiles(AppInfo, history.Files);
                    }
                    logForm.Close();
                }
            }
            catch (Exception ex)
            {
                Command_Display_Error("Patch the game", ex: ex);
            }
            finally
            {
                State.Value = LaunchManagerState.Idle;
                if (Preferences.OpenLogAfterPatch)
                {
                    Process.Start(_pathLogFile);
                }
            }
            return(history);
        }
Exemple #6
0
        public async void Command_Launch_Modded()
        {
            Action <IBindable <LaunchManagerState> > p = null;
            var history = await Command_Patch();

            p = v => {
                if (v.Value == LaunchManagerState.Idle)
                {
                    PatchingHelper.RestorePatchedFiles(AppInfo, history.Files);
                    State.HasChanged -= p;
                }
            };
            State.HasChanged += p;
            Command_Launch();
        }
 /// <exception cref="T:PatchworkLauncher.PatchingProcessException">Cannot switch files safely during the patching process</exception>
 public static void TrySwitchFilesSafely(string sourcePath, string destinationPath, string backupPath, PatchGroup patchGroup)
 {
     try
     {
         PatchingHelper.SwitchFilesSafely(sourcePath, destinationPath, backupPath);
     }
     catch (Exception exception)
     {
         throw new PatchingProcessException(exception)
               {
                   AssociatedInstruction = null,
                   AssociatedPatchGroup  = patchGroup,
                   Step = PatchProcessingStep.PerformingSwitch
               };
     }
 }
Exemple #8
0
        private void ApplyInstructions(IEnumerable <PatchGroup> patchGroups, ProgressObject po)
        {
            //TODO: Use a different progress tracking system and make the entire patching operation more recoverable and fault-tolerant.
            //TODO: Refactor this method.
            patchGroups = patchGroups.ToList();
            var appInfo      = AppInfo;
            var logger       = Logger;
            var fileProgress = new ProgressObject();

            po.Child.Value = fileProgress;
            var patchProgress = new ProgressObject();

            fileProgress.Child.Value = patchProgress;
            var myAttributesAssembly   = typeof(AppInfo).Assembly;
            var attributesAssemblyName = Path.GetFileName(myAttributesAssembly.Location);
            var history = new List <XmlFileHistory>();

            po.TaskTitle.Value = "Patching Game";
            po.TaskText.Value  = appInfo.AppName;
            po.Total.Value     = patchGroups.Count();

            foreach (var patchGroup in patchGroups)
            {
                var patchCount = patchGroup.Instructions.Count;
                po.TaskTitle.Value = $"Patching {appInfo.AppName}";
                var targetFile = patchGroup.TargetPath;
                po.TaskText.Value = Path.GetFileName(targetFile);
                //Note that Path.Combine(FILENAME, "..", OTHER_FILENAME) doesn't work on Mono but does work on .NET.
                var dir = Path.GetDirectoryName(targetFile);

                var localAssemblyName = Path.Combine(dir, attributesAssemblyName);
                var copy = true;
                fileProgress.TaskTitle.Value = "Patching File";
                fileProgress.Total.Value     = 2 + patchCount;
                fileProgress.Current.Value++;

                var backupModified = PatchingHelper.GetBackupForModified(targetFile);
                var backupOrig     = PatchingHelper.GetBackupForOriginal(targetFile);
                fileProgress.TaskText.Value = "Applying Patch";

                if (!PatchingHelper.DoesFileMatchPatchList(backupModified, targetFile, patchGroup.Instructions) ||
                    Preferences.AlwaysPatch)
                {
                    if (File.Exists(localAssemblyName))
                    {
                        try {
                            var localAssembly = AssemblyCache.Default.ReadAssembly(localAssemblyName);
                            if (localAssembly.GetAssemblyMetadataString() == myAttributesAssembly.GetAssemblyMetadataString())
                            {
                                copy = false;
                            }
                        }
                        catch (Exception ex) {
                            Logger.Warning(ex, $"Failed to read local attributes assembly so it will be overwritten.");
                            //if reading the assembly failed for any reason, just ignore...
                        }
                    }
                    if (copy)
                    {
                        File.Copy(myAttributesAssembly.Location, localAssemblyName, true);
                    }

                    var patcher = new AssemblyPatcher(targetFile, logger)
                    {
                        EmbedHistory = true
                    };

                    foreach (var patch in patchGroup.Instructions)
                    {
                        try {
                            patcher.PatchManifest(patch.Patch, patchProgress.ToMonitor());
                        }
                        catch (PatchException ex) {
                            throw new PatchingProcessException(ex)
                                  {
                                      AssociatedInstruction = patch,
                                      AssociatedPatchGroup  = patchGroup,
                                      Step = PatchProcessingStep.ApplyingSpecificPatch
                                  };
                        }
                        fileProgress.Current.Value++;
                    }
                    patchProgress.TaskText.Value  = "";
                    patchProgress.TaskTitle.Value = "";


                    fileProgress.Current.Value++;
                    fileProgress.TaskText.Value = "Writing Assembly";
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        fileProgress.TaskText.Value = "Running PEVerify...";
                        var targetFolder = Path.GetDirectoryName(targetFile);
                        try {
                            var peOutput = patcher.RunPeVerify(new PEVerifyInput {
                                AssemblyResolutionFolder = targetFolder,
                                IgnoreErrors             = AppInfo.IgnorePEVerifyErrors.ToList()
                            });
                            logger.Information(peOutput.Output);
                        }
                        catch (Exception ex) {
                            logger.Error(ex, "Failed to run PEVerify on the assembly.");
                        }
                    }

                    try {
                        patcher.WriteTo(backupModified);
                    }
                    catch (Exception ex) {
                        throw new PatchingProcessException(ex)
                              {
                                  AssociatedInstruction = null,
                                  AssociatedPatchGroup  = patchGroup,
                                  Step = PatchProcessingStep.WritingToFile
                              };
                    }
                }
                else
                {
                    fileProgress.Current.Value += patchCount;
                }
                try {
                    PatchingHelper.SwitchFilesSafely(backupModified, targetFile, backupOrig);
                }
                catch (Exception ex) {
                    throw new PatchingProcessException(ex)
                          {
                              AssociatedInstruction = null,
                              AssociatedPatchGroup  = patchGroup,
                              Step = PatchProcessingStep.PerformingSwitch
                          };
                }
                AssemblyCache.Default.ClearCache();
                po.Current.Value++;
            }
        }
Exemple #9
0
        public async void Command_TestRun()
        {
            var history = await Command_Patch();

            PatchingHelper.RestorePatchedFiles(AppInfo, history.Files);
        }
Exemple #10
0
        public LaunchManager()
        {
            //the following is needed on linux... the current directory must be the Mono executable, which is bad.
            Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            try {
                FormIcon = Icon.FromHandle(Resources.IconSmall.GetHicon());
                if (File.Exists(_pathLogFile))
                {
                    File.Delete(_pathLogFile);
                }
                XmlPreferences prefs = new XmlPreferences();
                try {
                    prefs = _prefsSerializer.Deserialize(_pathPrefsFile, new XmlPreferences());
                }
                catch (Exception ex) {
                    Command_Display_Error("Read preferences file", _pathPrefsFile, ex, "Special preferences will be reset");
                }
                Preferences = prefs;
                Logger      =
                    new LoggerConfiguration().WriteTo.File(_pathLogFile, LogEventLevel.Verbose).MinimumLevel.Is(Preferences.MinimumEventLevel).CreateLogger();
                AppInfoFactory gameInfoFactory;

                gameInfoFactory = !File.Exists(_pathGameInfoAssembly) ? null : PatchingHelper.LoadAppInfoFactory(_pathGameInfoAssembly);


                var settings = new XmlSettings();
                var history  = new XmlHistory();
                ;
                try {
                    history = _historySerializer.Deserialize(_pathHistoryXml, new XmlHistory());
                }
                catch (Exception ex) {
                    Command_Display_Error("Load patching history", _pathHistoryXml, ex,
                                          "If the launcher was terminated unexpectedly last time, it may not be able to recover.");
                }

                try {
                    settings = _settingsSerializer.Deserialize(_pathSettings, new XmlSettings());
                }
                catch (Exception ex) {
                    Command_Display_Error("Read settings file", _pathSettings, ex, "Patch list and other settings will be reset.");
                }

                string folderDialogReason = null;
                if (settings.BaseFolder == null)
                {
                    folderDialogReason = "(no game folder has been specified)";
                }
                else if (!Directory.Exists(settings.BaseFolder))
                {
                    folderDialogReason = "(the previous game folder does not exist)";
                }
                if (folderDialogReason != null)
                {
                    if (!Command_SetGameFolder_Dialog(folderDialogReason))
                    {
                        Command_ExitApplication();
                    }
                }
                else
                {
                    BaseFolder = settings.BaseFolder;
                }
                _home = new guiHome(this)
                {
                    Icon = FormIcon
                };
                var defaultAppInfo = new AppInfo()
                {
                    AppName = "No AppInfo.dll",
                };
                AppInfo            = gameInfoFactory?.CreateInfo(new DirectoryInfo(BaseFolder)) ?? defaultAppInfo;
                AppInfo.AppVersion = AppInfo.AppVersion ?? "version??";
                var icon = TryOpenIcon(AppInfo.IconLocation) ?? _home.Icon.ToBitmap();
                ProgramIcon  = icon;
                Instructions = new DisposingBindingList <PatchInstruction>();
                var instructions = new List <XmlInstruction>();
                foreach (var xmlPatch in settings.Instructions)
                {
                    try {
                        Command_Direct_AddPatch(xmlPatch.PatchPath, xmlPatch.IsEnabled);
                    }
                    catch {
                        instructions.Add(xmlPatch);
                    }
                }
                var patchList = instructions.Select(x => x.PatchPath).Join(Environment.NewLine);
                if (patchList.Length > 0)
                {
                    Command_Display_Error("Load patches on startup.", patchList);
                }
                try {
                    PatchingHelper.RestorePatchedFiles(AppInfo, history.Files);
                }
                catch (Exception ex) {
                    Command_Display_Error("Restore files", ex: ex);
                }
                //File.Delete(_pathHistoryXml);

                _home.Closed += (sender, args) => Command_ExitApplication();
                _icon         = new NotifyIcon {
                    Icon        = FormIcon,
                    Visible     = false,
                    Text        = "Patchwork Launcher",
                    ContextMenu = new ContextMenu {
                        MenuItems =
                        {
                            new MenuItem("Quit", (o, e) => Command_ExitApplication())
                        }
                    }
                };
                File.Delete(_pathHistoryXml);
            }
            catch (Exception ex) {
                Command_Display_Error("Launch the application", ex: ex, message: "The application will now exit.");
                Command_ExitApplication();
            }
        }
Exemple #11
0
 private static AppInfoFactory GetFactory(string filePath)
 {
     return(File.Exists(filePath) ? PatchingHelper.LoadAppInfoFactory(filePath) : null);
 }
Exemple #12
0
        public static void ApplyInstructions(LaunchType launchType, List <PatchGroup> patchGroups, ProgressObject totalProgress)
        {
            //TODO: Use a different progress tracking system and make the entire patching operation more recoverable and fault-tolerant.
            //TODO: Refactor this method.
            AppInfo appInfo = AppContextManager.Context.Value;

            Assembly myAttributesAssembly     = typeof(AppInfo).Assembly;
            string   myAttributesAssemblyName = Path.GetFileName(myAttributesAssembly.Location);

            var fileProgress  = new ProgressObject();
            var patchProgress = new ProgressObject();

            totalProgress.Child.Value = fileProgress;
            fileProgress.Child.Value  = patchProgress;

            totalProgress.SetTaskData("Patching Game", appInfo.AppName, patchGroups.Count);

            foreach (PatchGroup patchGroup in patchGroups)
            {
                int patchCount = patchGroup.Instructions.Count;

                string destinationPath = patchGroup.TargetPath;

                var patcher = new AssemblyPatcher(destinationPath, Logger);
                patcher.EmbedHistory = true;

                string sourcePath = PatchingHelper.GetBackupForModified(destinationPath);
                string backupPath = PatchingHelper.GetBackupForOriginal(destinationPath);

                // note that Path.Combine(FILENAME, "..", OTHER_FILENAME) doesn't work on Mono but does work on .NET.
                string targetDirectory = Path.GetDirectoryName(destinationPath);

                string localAssemblyName = Path.Combine(targetDirectory, myAttributesAssemblyName);

                totalProgress.SetTaskData(string.Format("Patching {0}", appInfo.AppName), Path.GetFileName(destinationPath));

                fileProgress.SetTaskData("Patching File", total: 2 + patchCount, increment: true);
                fileProgress.SetTaskData(taskText: "Applying Patch");

                if (!PatchingHelper.DoesFileMatchPatchList(sourcePath, destinationPath, patchGroup.Instructions) || PreferencesManager.Preferences.AlwaysPatch)
                {
                    try
                    {
                        myAttributesAssembly.TryCopyAttributesAssembly(localAssemblyName);
                    }
                    catch (Exception exception)
                    {
                        Logger.Warning(exception, "Failed to read local attributes assembly so it will be overwritten.");
                    }

                    foreach (PatchInstruction patch in patchGroup.Instructions)
                    {
                        try
                        {
                            patcher.TryPatchManifest(patch, patchGroup, patchProgress);
                        }
                        catch (PatchingProcessException exception)
                        {
                            Logger.Show(exception);
                        }

                        fileProgress.SetTaskData(increment: true);
                    }

                    patchProgress.SetTaskData(string.Empty, string.Empty);

                    fileProgress.SetTaskData(taskText: "Writing Assembly", increment: true);

                    if (launchType == LaunchType.Test && Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        fileProgress.SetTaskData(taskText: "Running PEVerify");

                        string peOutput = patcher.TryRunPeVerify(appInfo, destinationPath);

                        Logger.Information(peOutput);
                    }

                    try
                    {
                        patcher.TryBackup(sourcePath, patchGroup);
                    }
                    catch (PatchingProcessException exception)
                    {
                        Logger.Show(exception);
                    }
                }
                else
                {
                    fileProgress.Current.Value += patchCount;
                }

                try
                {
                    LaunchManager.TrySwitchFilesSafely(sourcePath, destinationPath, backupPath, patchGroup);
                }
                catch (PatchingProcessException exception)
                {
                    Logger.Show(exception);
                }

                AssemblyCache.Default.ClearCache();

                totalProgress.SetTaskData(increment: true);
            }
        }