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); } }