private static string GetPlotManagerPath(GameTarget target) { switch (target.Game) { case MEGame.ME1: return(Path.Combine(M3Directories.GetCookedPath(target), @"PlotManager.u")); case MEGame.LE1: case MEGame.ME2: case MEGame.LE2: return(Path.Combine(M3Directories.GetCookedPath(target), @"PlotManager.pcc")); } return(null); }
public bool ApplyMergeMod(Mod associatedMod, GameTarget target, ref int numTotalDone, int numTotalMerges, Action <int, int, string, string> mergeProgressDelegate = null) { Log.Information($@"Applying {MergeModFilename}"); var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, true, gameRootOverride: target.TargetPath); if (target.Game == MEGame.LE2) { // SPECIAL CASE: LE2 EntryMenu is loaded before DLC version so first load of the file // will be basegame one. The majority of time this is likely the desirable // file so we only target this one instead. loadedFiles[@"EntryMenu.pcc"] = Path.Combine(M3Directories.GetCookedPath(target), @"EntryMenu.pcc"); } int numDone = 0; foreach (var mf in FilesToMergeInto) { mf.ApplyChanges(target, loadedFiles, associatedMod, ref numTotalDone, numTotalMerges, mergeProgressDelegate); numDone++; } return(true); }
public void TestM3DirectoryResults() { GlobalTest.Init(); List <GameTarget> targets = new List <GameTarget>(); var root = GlobalTest.GetTestGameFoldersDirectory(MEGame.ME1); foreach (var d in Directory.GetDirectories(root)) { GameTarget gt = new GameTarget(MEGame.ME1, d, false, false); gt.ValidateTarget(); if (gt.IsValid) { targets.Add(gt); } } root = GlobalTest.GetTestGameFoldersDirectory(MEGame.ME2); foreach (var d in Directory.GetDirectories(root)) { GameTarget gt = new GameTarget(MEGame.ME2, d, false, false); gt.ValidateTarget(); if (gt.IsValid) { targets.Add(gt); } } root = GlobalTest.GetTestGameFoldersDirectory(MEGame.ME3); foreach (var d in Directory.GetDirectories(root)) { GameTarget gt = new GameTarget(MEGame.ME3, d, false, false); gt.ValidateTarget(); if (gt.IsValid) { targets.Add(gt); } } foreach (var target in targets) { string expectedDLCPath; string expectedASIPath; string expectedBioGamePath; string expectedCookedPath; string expectedExecutableDir; if (target.Game == MEGame.ME1) { expectedDLCPath = Path.Combine(target.TargetPath, @"DLC"); expectedASIPath = Path.Combine(target.TargetPath, @"Binaries", @"asi"); expectedBioGamePath = Path.Combine(target.TargetPath, @"BioGame"); expectedCookedPath = Path.Combine(target.TargetPath, @"BioGame", @"CookedPC"); expectedExecutableDir = Path.Combine(target.TargetPath, @"Binaries"); } else if (target.Game == MEGame.ME2) { expectedDLCPath = Path.Combine(target.TargetPath, @"BioGame", @"DLC"); expectedASIPath = Path.Combine(target.TargetPath, @"Binaries", @"asi"); expectedBioGamePath = Path.Combine(target.TargetPath, @"BioGame"); expectedCookedPath = Path.Combine(target.TargetPath, @"BioGame", @"CookedPC"); expectedExecutableDir = Path.Combine(target.TargetPath, @"Binaries"); } else { expectedDLCPath = Path.Combine(target.TargetPath, @"BIOGame", @"DLC"); expectedASIPath = Path.Combine(target.TargetPath, @"Binaries", @"Win32", @"asi"); expectedBioGamePath = Path.Combine(target.TargetPath, @"BIOGame"); expectedCookedPath = Path.Combine(target.TargetPath, @"BIOGame", @"CookedPCConsole"); expectedExecutableDir = Path.Combine(target.TargetPath, @"Binaries", @"Win32"); } Assert.AreEqual(expectedDLCPath, M3Directories.GetDLCPath(target)); Assert.AreEqual(expectedASIPath, M3Directories.GetASIPath(target)); Assert.AreEqual(expectedBioGamePath, M3Directories.GetBioGamePath(target)); Assert.AreEqual(expectedCookedPath, M3Directories.GetCookedPath(target)); Assert.AreEqual(expectedExecutableDir, M3Directories.GetExecutableDirectory(target)); } }
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 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 (Enumerable.Any(failedApplications)) { //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(MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream, false, true); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackageFromStream(decompressedStream, $@"Vanilla - {Path.GetFileName(file.Key)}"); //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackageFromStream(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream var targetFile = Path.Combine(M3Directories.GetCookedPath(SelectedInstallTarget), Path.GetFileName(file.Key)); var targetPackage = MEPackageHandler.OpenMEPackage(targetFile); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { targetPackage.Save(compress: true); 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(M3Directories.GetDLCPath(SelectedInstallTarget), dlcFolderName, @"CookedPCConsole"); var sfar = mapping.Key == ModJob.JobHeader.TESTPATCH ? M3Directories.GetTestPatchSFARPath(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.OpenMEPackageFromStream(decompressedStream, $@"VanillaDLC - {Path.GetFileName(file.Key)}"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackageFromStream(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.OpenMEPackageFromStream(targetFileStream, $@"Target package {dlcFolderName} - {file.Key}, from SFAR: {unpacked}"); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { if (unpacked) { targetPackage.Save(Path.Combine(targetCookedPCDir, file.Key)); Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath); } else { var finalSTream = targetPackage.SaveToStream(false); // No compress. Not sure if we should support doing that though. targetDLCPackage.ReplaceEntry(finalSTream.ToArray(), targetDLCPackage.FindFileEntry(Path.GetFileName(file.Key))); Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath); } } else { Log.Error(@"Could not 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 = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, percent); //Run autotoc void tocingUpdate(int percent) { BottomLeftMessage = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, 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) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } 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 = M3L.GetString(M3L.string_mixinsInstalledMaybe); //} }; CompilePanelButton.IsOpen = false; nbw.RunWorkerAsync(); }