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