Example #1
0
        public void TestMixins()
        {
            GlobalTest.Init();

            var me3BackupPath = BackupService.GetGameBackupPath(MEGame.ME3);

            if (me3BackupPath != null)
            {
                GlobalTest.CreateScratchDir();
                MixinHandler.LoadME3TweaksPackage();
                // We can conduct this test
                var mixins = MixinHandler.ME3TweaksPackageMixins.Where(x => !x.IsFinalizer).ToList();
                MixinHandler.LoadPatchDataForMixins(mixins);

                List <string> failedMixins = new List <string>();
                void failedApplicationCallback(string str)
                {
                    failedMixins.Add(str);
                }

                var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback);

                //Mixins are ready to be applied
                var outdir = Path.Combine(Path.Combine(GlobalTest.GetScratchDir(), "MixinTest"));
                Utilities.DeleteFilesAndFoldersRecursively(outdir);
                Directory.CreateDirectory(outdir);
                foreach (var mapping in compilingListsPerModule)
                {
                    MixinManager.ApplyMixinsToModule(mapping, outdir, null, failedApplicationCallback);
                }

                MixinHandler.FreeME3TweaksPatchData();
                GlobalTest.DeleteScratchDir();
                if (failedMixins.Any())
                {
                    Assert.Fail($"MixinTests failed. {failedMixins.Count} mixins failed to apply.");
                }
            }
            else
            {
                Console.WriteLine(@"No backup for ME3 is available. MixinTests will be skipped.");
            }
        }
Example #2
0
        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();
        }
Example #3
0
        private void CompileAsNewMod()
        {
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileAsNewModThread");
            List <string>         failedApplications = new List <string>();
            var modname = NewModName;
            var modpath = Path.Combine(Utilities.GetME3ModsDirectory(), Utilities.SanitizePath(modname));
            var result  = M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_dialogCreatingNewModWithExistingName, NewModName, modpath), M3L.GetString(M3L.string_modAlreadyExists), MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);

            if (result == MessageBoxResult.No)
            {
                Log.Information(@"User has aborted mixin compilation due to same-named mod existing");
                return; //abort.
            }



            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
                    modpath = null;
                    Log.Information(@"Aborting mixin compiling due to incompatible selection of mixins");
                    return;
                }

                if (Directory.Exists(modpath))
                {
                    Utilities.DeleteFilesAndFoldersRecursively(modpath);
                }

                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 packageAsStream =
                                          VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME3,
                                                                                   Path.GetFileName(file.Key));
                                //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc");
                                using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream, true);
                                //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc");

                                using var finalStream = MixinHandler.ApplyMixins(decompressedStream, file.Value,
                                                                                 completedSingleApplicationCallback, failedApplicationCallback);
                                CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler);
                                finalStream.Position = 0;
                                var package          = MEPackageHandler.OpenMEPackage(finalStream);
                                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.
                        foreach (var file in mapping.Value)
                        {
                            try
                            {
                                using var packageAsStream =
                                          VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage);
                                using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream);
                                using var finalStream        = MixinHandler.ApplyMixins(decompressedStream, file.Value, completedSingleApplicationCallback, failedApplicationCallback);
                                CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler);
                                finalStream.Position = 0;
                                var package          = MEPackageHandler.OpenMEPackage(finalStream);
                                var outfile          = Path.Combine(outdir, Path.GetFileName(file.Key));
                                package.save(outfile, true);
                            }
                            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();

                //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 = M3L.GetString(M3L.string_selectMixinsToCompile);
                }
            };
            CompilePanelButton.IsOpen = false;
            nbw.RunWorkerAsync();
        }
        private void CompileAsNewMod()
        {
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileAsNewModThread");
            List <string>         failedApplications = new List <string>();
            var modname = NewModName;
            var modpath = Path.Combine(Utilities.GetME3ModsDirectory(), Utilities.SanitizePath(modname));

            if (Directory.Exists(modpath))
            {
                var result = M3L.ShowDialog(mainwindow,
                                            M3L.GetString(M3L.string_interp_dialogCreatingNewModWithExistingName, NewModName, modpath),
                                            M3L.GetString(M3L.string_modAlreadyExists), MessageBoxButton.YesNo, MessageBoxImage.Warning,
                                            MessageBoxResult.No);
                if (result == MessageBoxResult.No)
                {
                    Log.Information(@"User has aborted mixin compilation due to same-named mod existing");
                    return; //abort.
                }
            }



            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
                    modpath = null;
                    Log.Information(@"Aborting mixin compiling due to incompatible selection of mixins");
                    return;
                }

                if (Directory.Exists(modpath))
                {
                    Utilities.DeleteFilesAndFoldersRecursively(modpath);
                }

                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 =>
                {
                    ApplyMixinsToModule(mapping, modpath, completedSingleApplicationCallback, failedApplicationCallback);
                });

                MixinHandler.FreeME3TweaksPatchData();

                //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_selectMixinsToCompile);
                }
            };
            CompilePanelButton.IsOpen = false;
            nbw.RunWorkerAsync();
        }