private void RestoreAllBasegame() { bool restore = false; if (SelectedTarget.TextureModded) { if (!Settings.DeveloperMode) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_dialogRestoringFilesWhileAlotIsInstalledNotAllowed), M3L.GetString(M3L.string_cannotRestoreSfarFiles), MessageBoxButton.OK, MessageBoxImage.Error); return; } else { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_dialogRestoringFilesWhileAlotIsInstalledNotAllowedDevMode), M3L.GetString(M3L.string_invalidTexturePointersWarning), MessageBoxButton.YesNo, MessageBoxImage.Warning); restore = res == MessageBoxResult.Yes; } } else { restore = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoreAllModifiedFilesQuestion), M3L.GetString(M3L.string_confirmRestoration), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes; } if (restore) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"RestoreAllBasegameFilesThread"); nbw.DoWork += (a, b) => { RestoreAllBasegameInProgress = true; var restorableFiles = SelectedTarget.ModifiedBasegameFiles.Where(x => x.CanRestoreFile()).ToList(); //Set UI states foreach (var v in restorableFiles) { v.Restoring = true; } //Restore files foreach (var v in restorableFiles) //to list will make sure this doesn't throw concurrent modification { v.RestoreFile(true); } }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } RestoreAllBasegameInProgress = false; if (SelectedTarget.Game == MEGame.ME3) { AutoTOC.RunTOCOnGameTarget(SelectedTarget); } CommandManager.InvalidateRequerySuggested(); }; nbw.RunWorkerAsync(); } }
/// <summary> /// Sets the TypeKey and ConsoleKey values for an ME3 game target. This method is synchronous. /// </summary> /// <param name="target"></param> /// <param name="consoleKeyStr"></param> /// <param name="typeKeyStr"></param> public static void SetME3ConsoleKeybinds(GameTarget target, string consoleKeyStr = null, string typeKeyStr = null) { var coalPath = Path.Combine(target.TargetPath, @"BioGame", @"CookedPCConsole", @"Coalesced.bin"); Dictionary <string, string> coalescedFilemapping = null; if (File.Exists(coalPath)) { using FileStream fs = new FileStream(coalPath, FileMode.Open); coalescedFilemapping = MassEffect3.Coalesce.Converter.DecompileToMemory(fs); } else { Log.Error(@"Could not get file data for coalesced chunk BASEGAME as Coalesced.bin file was missing"); return; } var bioinputText = coalescedFilemapping[@"BioInput.xml"]; var coalFileDoc = XDocument.Parse(bioinputText); var consolekey = coalFileDoc.XPathSelectElement(@"/CoalesceAsset/Sections/Section[@name='engine.console']/Property[@name='consolekey']"); var typekey = coalFileDoc.XPathSelectElement(@"/CoalesceAsset/Sections/Section[@name='engine.console']/Property[@name='typekey']"); if (consolekey != null && consoleKeyStr != null) { consolekey.Value = consoleKeyStr; } else { var consoleElement = coalFileDoc.XPathSelectElement(@"/CoalesceAsset/Sections/Section[@name='engine.console']"); var consoleKeyElement = new XElement(@"Property", consoleKeyStr); consoleKeyElement.SetAttributeValue(@"name", @"consolekey"); consoleElement.Add(consoleKeyElement); } if (typekey != null && typeKeyStr != null) { typekey.Value = typeKeyStr; } else { var consoleElement = coalFileDoc.XPathSelectElement(@"/CoalesceAsset/Sections/Section[@name='engine.console']"); var consoleKeyElement = new XElement(@"Property", typeKeyStr); consoleKeyElement.SetAttributeValue(@"name", @"typekey"); consoleElement.Add(consoleKeyElement); } coalescedFilemapping[@"BioInput.xml"] = coalFileDoc.ToString(); var recompiled = MassEffect3.Coalesce.Converter.CompileFromMemory(coalescedFilemapping); recompiled.WriteToFile(coalPath); AutoTOC.RunTOCOnGameTarget(target); }
private void CompileIntoGame() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileIntoGameThread"); List <string> failedApplications = new List <string>(); nbw.DoWork += (a, b) => { BottomLeftMessage = M3L.GetString(M3L.string_compilingMixins); OperationInProgress = true; //DEBUG STUFF #if DEBUG int numCoresToApplyWith = 1; #else var numCoresToApplyWith = Environment.ProcessorCount; if (numCoresToApplyWith > 4) { numCoresToApplyWith = 4; //no more than 4 as this uses a lot of memory } #endif var mixins = AvailableOfficialMixins.Where(x => x.UISelectedForUse).ToList(); MixinHandler.LoadPatchDataForMixins(mixins); //before dynamic void failedApplicationCallback(string str) { failedApplications.Add(str); } var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback); if (failedApplications.Any()) { //Error building list Log.Information(@"Aborting mixin install due to incompatible selection of mixins"); return; } ProgressBarMax = mixins.Count(); ProgressBarValue = 0; int numdone = 0; void completedSingleApplicationCallback() { var val = Interlocked.Increment(ref numdone); ProgressBarValue = val; } //Mixins are ready to be applied Parallel.ForEach(compilingListsPerModule, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount > numCoresToApplyWith ? numCoresToApplyWith : Environment.ProcessorCount }, mapping => { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); //var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); //Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream, true); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackage(decompressedStream, $@"Vanilla - {Path.GetFileName(file.Key)}"); //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackage(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream var targetFile = Path.Combine(MEDirectories.CookedPath(SelectedInstallTarget), Path.GetFileName(file.Key)); var targetPackage = MEPackageHandler.OpenMEPackage(targetFile); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { targetPackage.save(); Log.Information("Three way merge succeeded for " + targetFile); } else { Log.Error("Could not merge three way merge into " + targetFile); } //var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); //package.save(outfile, false); // don't compress //finalStream.WriteToFile(outfile); //File.WriteAllBytes(outfile, finalStream.ToArray()); } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. var targetCookedPCDir = Path.Combine(MEDirectories.DLCPath(SelectedInstallTarget), dlcFolderName, @"CookedPCConsole"); var sfar = mapping.Key == ModJob.JobHeader.TESTPATCH ? Utilities.GetTestPatchPath(SelectedInstallTarget) : Path.Combine(targetCookedPCDir, @"Default.sfar"); bool unpacked = new FileInfo(sfar).Length == 32; DLCPackage targetDLCPackage = unpacked ? null : new DLCPackage(sfar); //cache SFAR target foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackage(decompressedStream, $@"VanillaDLC - {Path.GetFileName(file.Key)}"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackage(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream // must see if DLC is unpacked first MemoryStream targetFileStream = null; //Packed if (unpacked) { targetFileStream = new MemoryStream(File.ReadAllBytes(Path.Combine(targetCookedPCDir, file.Key))); } else { targetFileStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, Path.GetFileName(file.Key), forcedDLC: targetDLCPackage); } var targetPackage = MEPackageHandler.OpenMEPackage(targetFileStream, $@"Target package {dlcFolderName} - {file.Key}, from SFAR: {unpacked}"); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { if (unpacked) { targetPackage.save(); Log.Information("Three way merge succeeded for " + targetPackage.FilePath); } else { var finalSTream = targetPackage.saveToStream(); targetDLCPackage.ReplaceEntry(finalSTream.ToArray(), targetDLCPackage.FindFileEntry(Path.GetFileName(file.Key))); Log.Information("Three way merge succeeded for " + targetPackage.FileSourceForDebugging); } } else { Log.Error("Could not merge three way merge into " + targetFileStream); } } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }); MixinHandler.FreeME3TweaksPatchData(); var percent = 0; //this is used to save a localization BottomLeftMessage = $"Running AutoTOC on game {percent}%"; //Run autotoc void tocingUpdate(int percent) { BottomLeftMessage = $"Running AutoTOC on game {percent}%"; } AutoTOC.RunTOCOnGameTarget(SelectedInstallTarget, tocingUpdate); //Generate moddesc //IniData ini = new IniData(); //ini[@"ModManager"][@"cmmver"] = App.HighestSupportedModDesc.ToString(CultureInfo.InvariantCulture); //prevent commas //ini[@"ModInfo"][@"game"] = @"ME3"; //ini[@"ModInfo"][@"modname"] = modname; //ini[@"ModInfo"][@"moddev"] = App.AppVersionHR; //ini[@"ModInfo"][@"moddesc"] = M3L.GetString(M3L.string_compiledFromTheFollowingMixins); //ini[@"ModInfo"][@"modver"] = @"1.0"; //generateRepaceFilesMapping(ini, modpath); //File.WriteAllText(Path.Combine(modpath, @"moddesc.ini"), ini.ToString()); }; nbw.RunWorkerCompleted += (a, b) => { OperationInProgress = false; ClearMixinHandler(); if (failedApplications.Count > 0) { var ld = new ListDialog(failedApplications, M3L.GetString(M3L.string_failedToApplyAllMixins), M3L.GetString(M3L.string_theFollowingMixinsFailedToApply), mainwindow); ld.ShowDialog(); } /*if (modpath != null) * { * OnClosing(new DataEventArgs(modpath)); * } * else * {*/ BottomLeftMessage = "Mixins installed, maybe. Check logs"; //} }; CompilePanelButton.IsOpen = false; nbw.RunWorkerAsync(); }