Exemple #1
0
        /// <summary>
        /// Runs the Validation of a Modlist
        /// </summary>
        /// <returns></returns>
        protected override async Task <ExitCode> Run()
        {
            ModList modlist;

            try
            {
                modlist = AInstaller.LoadFromFile((AbsolutePath)Input);
            }
            catch (Exception e)
            {
                return(CLIUtils.Exit($"Error while loading the Modlist!\n{e}", ExitCode.Error));
            }

            if (modlist == null)
            {
                return(CLIUtils.Exit($"The Modlist could not be loaded!", ExitCode.Error));
            }


            var queue = new WorkQueue();

            try
            {
                ValidateModlist.RunValidation(modlist).RunSynchronously();
            }
            catch (Exception e)
            {
                return(CLIUtils.Exit($"Error during Validation!\n{e}", ExitCode.Error));
            }

            return(CLIUtils.Exit("The Modlist passed the Validation", 0));
        }
Exemple #2
0
        public ModListVM(string modListPath)
        {
            ModListPath = modListPath;
            try
            {
                SourceModList = AInstaller.LoadFromFile(modListPath);
            }
            catch (Exception ex)
            {
                Error = ex;
            }

            ImageObservable = Observable.Return(Unit.Default)
                              .ObserveOn(RxApp.TaskpoolScheduler)
                              .Select(filePath =>
            {
                try
                {
                    using (var fs = new FileStream(ModListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                        using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
                        {
                            var ms    = new MemoryStream();
                            var entry = ar.GetEntry("modlist-image.png");
                            if (entry == null)
                            {
                                return(default(MemoryStream));
                            }
                            using (var e = entry.Open())
                            {
                                e.CopyTo(ms);
                            }
                            return(ms);
                        }
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(MemoryStream));
                }
            })
                              .ObserveOn(RxApp.MainThreadScheduler)
                              .Select(memStream =>
            {
                if (memStream == null)
                {
                    return(default(BitmapImage));
                }
                try
                {
                    return(UIUtils.BitmapImageFromStream(memStream));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(BitmapImage));
                }
            })
                              .Replay(1)
                              .RefCount();
        }
Exemple #3
0
        protected override async Task <ExitCode> Run()
        {
            var file = (AbsolutePath)Input;

            var modlist = AInstaller.LoadFromFile(file);

            using var arch = new ZipArchive(await file.OpenRead(), ZipArchiveMode.Read);

            var reported = modlist.Directives
                           .Select(d =>
            {
                switch (d)
                {
                case CleanedESM esm:
                    {
                        var entry = arch.GetEntry(esm.SourceDataID.ToString());
                        return(entry.Length, d);
                    }

                case InlineFile inlined:
                    return(inlined.Size, d);

                case PatchedFromArchive pfa:
                    {
                        var entry = arch.GetEntry(pfa.PatchID.ToString());
                        return(entry.Length, d);
                    }

                default:
                    return(0, d);
                }
            })
                           .Where(f => f.Item1 != 0)
                           .OrderBy(f => f.Item1);

            foreach (var entry in reported)
            {
                switch (entry.d)
                {
                case CleanedESM esm:
                    Console.WriteLine($"{entry.Item1.ToFileSizeString()} for a cleaned ESM patch on {entry.d.To}");
                    break;

                case InlineFile ilined:
                    Console.WriteLine($"{entry.Item1.ToFileSizeString()} for a inlined file {entry.d.To}");
                    break;

                case PatchedFromArchive archive:
                    Console.WriteLine($"{entry.Item1.ToFileSizeString()} for a patch on {entry.d.To}");
                    break;

                default:
                    break;
                }
            }
            Console.WriteLine($"{reported.Count()} entries {reported.Sum(e => e.Item1).ToFileSizeString()} in total");
            return(0);
        }
        private async Task Install(MO2Compiler compiler)
        {
            var modlist   = AInstaller.LoadFromFile(compiler.ModListOutputFile);
            var installer = new MO2Installer(
                archive: compiler.ModListOutputFile,
                modList: modlist,
                outputFolder: utils.InstallFolder,
                downloadFolder: utils.DownloadsFolder);

            installer.GameFolder = utils.GameFolder;
            await installer.Begin();
        }
Exemple #5
0
        private static async Task EnqueueFromList(DBContext db, ModlistMetadata list, WorkQueue queue)
        {
            var existing = await db.ModListStatus.FindOneAsync(l => l.Id == list.Links.MachineURL);

            var modlist_path = Path.Combine(Consts.ModListDownloadFolder,
                                            list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlist_path))
            {
                if (File.Exists(modlist_path))
                {
                    File.Delete(modlist_path);
                }

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlist_path);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded ModList");
            }

            Utils.Log($"Loading {modlist_path}");

            var installer = AInstaller.LoadFromFile(modlist_path);

            var archives = installer.Archives;

            Utils.Log($"Found {archives.Count} archives in {installer.Name} to index");
            var searching = archives.Select(a => a.Hash).Distinct().ToArray();

            Utils.Log($"Looking for missing archives");
            var knownArchives = (await db.IndexedFiles.AsQueryable().Where(a => searching.Contains(a.Hash))
                                 .Select(d => d.Hash).ToListAsync()).ToDictionary(a => a);

            Utils.Log($"Found {knownArchives.Count} pre-existing archives");
            var missing = archives.Where(a => !knownArchives.ContainsKey(a.Hash)).ToList();

            Utils.Log($"Found {missing.Count} missing archives, enqueing indexing jobs");

            var jobs = missing.Select(a => new Job {
                Payload = new IndexJob {
                    Archive = a
                }, Priority = Job.JobPriority.Low
            });

            Utils.Log($"Writing jobs to the database");
            await db.Jobs.InsertManyAsync(jobs, new InsertManyOptions { IsOrdered = false });

            Utils.Log($"Done adding archives for {installer.Name}");
        }
        protected void Install(MO2Compiler compiler)
        {
            var modlist   = AInstaller.LoadFromFile(compiler.ModListOutputFile);
            var installer = new MO2Installer(
                archive: compiler.ModListOutputFile,
                modList: modlist,
                outputFolder: utils.InstallFolder,
                downloadFolder: utils.DownloadsFolder);

            installer.WarnOnOverwrite = false;
            installer.GameFolder      = utils.GameFolder;
            installer.Begin().Wait();
        }
        protected void Install(VortexCompiler vortexCompiler)
        {
            var modList   = AInstaller.LoadFromFile(vortexCompiler.ModListOutputFile);
            var installer = new MO2Installer(
                archive: vortexCompiler.ModListOutputFile,
                modList: modList,
                outputFolder: utils.InstallFolder,
                downloadFolder: utils.DownloadsFolder)
            {
                GameFolder = utils.GameFolder,
            };

            installer.Begin().Wait();
        }
        protected async Task Install(MO2Compiler compiler)
        {
            var modlist   = AInstaller.LoadFromFile(compiler.ModListOutputFile);
            var installer = new MO2Installer(
                archive: compiler.ModListOutputFile,
                modList: modlist,
                outputFolder: utils.InstallFolder,
                downloadFolder: utils.DownloadsFolder,
                parameters: SystemParametersConstructor.Create());

            installer.WarnOnOverwrite = false;
            installer.GameFolder      = utils.GameFolder;
            await installer.Begin();
        }
 protected async Task Install(VortexCompiler vortexCompiler)
 {
     var modList   = AInstaller.LoadFromFile(vortexCompiler.ModListOutputFile);
     var installer = new MO2Installer(
         archive: vortexCompiler.ModListOutputFile,
         modList: modList,
         outputFolder: utils.InstallFolder,
         downloadFolder: utils.DownloadsFolder,
         parameters: SystemParametersConstructor.Create())
     {
         GameFolder = utils.GameFolder,
     };
     await installer.Begin();
 }
Exemple #10
0
        /// <summary>
        /// Runs the Validation of a Modlist
        /// </summary>
        /// <param name="opts"></param>
        /// <returns>
        /// <para>
        /// <c>-1</c> bad Input
        /// <c>0</c> valid modlist
        /// <c>1</c> broken modlist
        /// </para>
        /// </returns>
        protected override async Task <int> Run()
        {
            if (!File.Exists(Input))
            {
                Console.WriteLine($"The file {Input} does not exist!");
                return(-1);
            }


            if (!Input.EndsWith(Consts.ModListExtension))
            {
                Console.WriteLine($"The file {Input} does not end with {Consts.ModListExtension}!");
                return(-1);
            }

            ModList modlist;

            try
            {
                modlist = AInstaller.LoadFromFile(Input);
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error while loading the Modlist!\n{e}");
                return(1);
            }

            if (modlist == null)
            {
                Console.WriteLine($"The Modlist could not be loaded!");
                return(1);
            }


            var queue = new WorkQueue();

            try
            {
                ValidateModlist.RunValidation(queue, modlist).RunSynchronously();
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error during Validation!\n{e}");
                return(1);
            }

            Console.WriteLine("The Modlist passed the Validation");
            return(0);
        }
Exemple #11
0
        private static async Task EnqueueFromList(SqlService sql, ModlistMetadata list, WorkQueue queue)
        {
            var modlistPath = Consts.ModListDownloadFolder.Combine(list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlistPath))
            {
                modlistPath.Delete();

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlistPath);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded ModList");
            }

            Utils.Log($"Loading {modlistPath}");

            var installer = AInstaller.LoadFromFile(modlistPath);

            var archives = installer.Archives;

            Utils.Log($"Found {archives.Count} archives in {installer.Name} to index");
            var searching = archives.Select(a => a.Hash).ToHashSet();

            Utils.Log($"Looking for missing archives");
            var knownArchives = await sql.FilterByExistingIndexedArchives(searching);

            Utils.Log($"Found {knownArchives.Count} pre-existing archives");
            var missing = archives.Where(a => !knownArchives.Contains(a.Hash)).ToList();

            Utils.Log($"Found {missing.Count} missing archives, enqueing indexing jobs");

            var jobs = missing.Select(a => new Job {
                Payload = new IndexJob {
                    Archive = a
                }, Priority = Job.JobPriority.Low
            });

            Utils.Log($"Writing jobs to the database");

            foreach (var job in jobs)
            {
                await sql.EnqueueJob(job);
            }

            Utils.Log($"Done adding archives for {installer.Name}");
        }
        protected async Task Install(MO2Compiler compiler)
        {
            Utils.Log("Loading Modlist");
            var modlist = AInstaller.LoadFromFile(compiler.ModListOutputFile);

            Utils.Log("Constructing Installer");
            var installer = new MO2Installer(
                archive: compiler.ModListOutputFile,
                modList: modlist,
                outputFolder: utils.InstallFolder,
                downloadFolder: utils.DownloadsFolder,
                parameters: CreateDummySystemParameters());

            installer.WarnOnOverwrite = false;
            installer.GameFolder      = utils.GameFolder;
            Utils.Log("Starting Install");
            await installer.Begin();
        }
Exemple #13
0
        private async Task ValidateList(SqlService sql, ModlistMetadata list, WorkQueue queue, ValidateModlist whitelists)
        {
            var modlistPath = Consts.ModListDownloadFolder.Combine(list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlistPath))
            {
                modlistPath.Delete();

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlistPath);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded modlist");
            }


            Utils.Log($"Loading {modlistPath}");

            var installer = AInstaller.LoadFromFile(modlistPath);

            Utils.Log($"{installer.Archives.Count} archives to validate");

            await DownloadDispatcher.PrepareAll(installer.Archives.Select(a => a.State));


            var validated = (await installer.Archives
                             .PMap(queue, async archive =>
            {
                var isValid = await IsValid(sql, whitelists, archive);

                return(new DetailedStatusItem {
                    IsFailing = !isValid, Archive = archive
                });
            }))
                            .ToList();


            var status = new DetailedStatus
            {
                Name             = list.Title,
                Archives         = validated.OrderBy(v => v.Archive.Name).ToList(),
                DownloadMetaData = list.DownloadMetadata,
                HasFailures      = validated.Any(v => v.IsFailing)
            };

            var dto = new ModListStatus
            {
                Id      = list.Links.MachineURL,
                Summary = new ModListSummary
                {
                    Name       = status.Name,
                    MachineURL = list.Links?.MachineURL ?? status.Name,
                    Checked    = status.Checked,
                    Failed     = status.Archives.Count(a => a.IsFailing),
                    Passed     = status.Archives.Count(a => !a.IsFailing),
                },
                DetailedStatus = status,
                Metadata       = list
            };

            Utils.Log(
                $"Writing Update for {dto.Summary.Name} - {dto.Summary.Failed} failed - {dto.Summary.Passed} passed");
            await sql.UpdateModListStatus(dto);

            Utils.Log(
                $"Done updating {dto.Summary.Name}");
        }
Exemple #14
0
        public ModListVM(AbsolutePath modListPath)
        {
            ModListPath = modListPath;
            try
            {
                SourceModList = AInstaller.LoadFromFile(modListPath);
            }
            catch (Exception ex)
            {
                Error = ex;
                Utils.Error(ex, "Exception while loading the modlist!");
            }

            ImageObservable = Observable.Return(Unit.Default)
                              // Download and retrieve bytes on background thread
                              .ObserveOn(RxApp.TaskpoolScheduler)
                              .SelectAsync(async filePath =>
            {
                try
                {
                    await using var fs = await ModListPath.OpenShared();
                    using var ar       = new ZipArchive(fs, ZipArchiveMode.Read);
                    var ms             = new MemoryStream();
                    var entry          = ar.GetEntry("modlist-image.png");
                    if (entry == null)
                    {
                        return(default(MemoryStream));
                    }
                    await using var e = entry.Open();
                    e.CopyTo(ms);
                    return(ms);
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(MemoryStream));
                }
            })
                              // Create Bitmap image on GUI thread
                              .ObserveOnGuiThread()
                              .Select(memStream =>
            {
                if (memStream == null)
                {
                    return(default(BitmapImage));
                }
                try
                {
                    return(UIUtils.BitmapImageFromStream(memStream));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(BitmapImage));
                }
            })
                              // If ever would return null, show WJ logo instead
                              .Select(x => x ?? ResourceLinks.WabbajackLogoNoText.Value)
                              .Replay(1)
                              .RefCount();
        }
        protected override async Task <ExitCode> Run()
        {
            var modListPath = (AbsolutePath)Modlist;

            if (modListPath.Extension != Consts.ModListExtension && modListPath.FileName != (RelativePath)"modlist.txt")
            {
                return(CLIUtils.Exit($"The file {Modlist} is not a valid modlist file!", ExitCode.BadArguments));
            }

            if (Copy && Move)
            {
                return(CLIUtils.Exit("You can't set both copy and move flags!", ExitCode.BadArguments));
            }

            var isModlist = modListPath.Extension == Consts.ModListExtension;

            var list = new List <TransferFile>();

            if (isModlist)
            {
                ModList modlist;

                try
                {
                    modlist = AInstaller.LoadFromFile(modListPath);
                }
                catch (Exception e)
                {
                    return(CLIUtils.Exit($"Error while loading the Modlist!\n{e}", ExitCode.Error));
                }

                if (modlist == null)
                {
                    return(CLIUtils.Exit("The Modlist could not be loaded!", ExitCode.Error));
                }

                CLIUtils.Log($"Modlist contains {modlist.Archives.Count} archives.");

                modlist.Archives.Do(a =>
                {
                    var inputPath  = Path.Combine(Input, a.Name);
                    var outputPath = Path.Combine(Output, a.Name);

                    if (!File.Exists(inputPath))
                    {
                        CLIUtils.Log($"File {inputPath} does not exist, skipping.");
                        return;
                    }

                    CLIUtils.Log($"Adding {inputPath} to the transfer list.");
                    list.Add(new TransferFile(inputPath, outputPath));

                    var metaInputPath  = Path.Combine(inputPath, ".meta");
                    var metaOutputPath = Path.Combine(outputPath, ".meta");

                    if (File.Exists(metaInputPath))
                    {
                        CLIUtils.Log($"Found meta file {metaInputPath}");
                        if (IncludeMeta)
                        {
                            CLIUtils.Log($"Adding {metaInputPath} to the transfer list.");
                            list.Add(new TransferFile(metaInputPath, metaOutputPath));
                        }
                        else
                        {
                            CLIUtils.Log($"Meta file {metaInputPath} will be ignored.");
                        }
                    }
                    else
                    {
                        CLIUtils.Log($"Found no meta file for {inputPath}");
                        if (IncludeMeta)
                        {
                            if (string.IsNullOrWhiteSpace(a.Meta))
                            {
                                CLIUtils.Log($"Meta for {a.Name} is empty, this should not be possible but whatever.");
                                return;
                            }

                            CLIUtils.Log("Adding meta from archive info the transfer list");
                            list.Add(new TransferFile(a.Meta, metaOutputPath, true));
                        }
                        else
                        {
                            CLIUtils.Log($"Meta will be ignored for {a.Name}");
                        }
                    }
                });
            }
            else
            {
                if (!Directory.Exists(Mods))
                {
                    return(CLIUtils.Exit($"Mods directory {Mods} does not exist!", ExitCode.BadArguments));
                }

                CLIUtils.Log($"Reading modlist.txt from {Modlist}");
                string[] modlist = File.ReadAllLines(Modlist);

                if (modlist == null || modlist.Length == 0)
                {
                    return(CLIUtils.Exit($"Provided modlist.txt file at {Modlist} is empty or could not be read!", ExitCode.BadArguments));
                }

                var mods = modlist.Where(s => s.StartsWith("+")).Select(s => s.Substring(1)).ToHashSet();

                if (mods.Count == 0)
                {
                    return(CLIUtils.Exit("Counted mods from modlist.txt are 0!", ExitCode.BadArguments));
                }

                CLIUtils.Log($"Found {mods.Count} mods in modlist.txt");

                var downloads = new HashSet <string>();

                Directory.EnumerateDirectories(Mods, "*", SearchOption.TopDirectoryOnly)
                .Where(d => mods.Contains(Path.GetRelativePath(Path.GetDirectoryName(d), d)))
                .Do(d =>
                {
                    var meta = Path.Combine(d, "meta.ini");
                    if (!File.Exists(meta))
                    {
                        CLIUtils.Log($"Mod meta file {meta} does not exist, skipping");
                        return;
                    }

                    string[] ini = File.ReadAllLines(meta);
                    if (ini == null || ini.Length == 0)
                    {
                        CLIUtils.Log($"Mod meta file {meta} could not be read or is empty!");
                        return;
                    }

                    ini.Where(i => !string.IsNullOrWhiteSpace(i) && i.StartsWith("installationFile="))
                    .Select(i => i.Replace("installationFile=", ""))
                    .Do(i =>
                    {
                        CLIUtils.Log($"Found installationFile {i}");
                        downloads.Add(i);
                    });
                });

                CLIUtils.Log($"Found {downloads.Count} installationFiles from mod metas.");

                Directory.EnumerateFiles(Input, "*", SearchOption.TopDirectoryOnly)
                .Where(f => downloads.Contains(Path.GetFileNameWithoutExtension(f)))
                .Do(f =>
                {
                    CLIUtils.Log($"Found archive {f}");

                    var outputPath = Path.Combine(Output, Path.GetFileName(f));

                    CLIUtils.Log($"Adding {f} to the transfer list");
                    list.Add(new TransferFile(f, outputPath));

                    var metaInputPath = Path.Combine(f, ".meta");
                    if (File.Exists(metaInputPath))
                    {
                        CLIUtils.Log($"Found meta file for {f} at {metaInputPath}");
                        if (IncludeMeta)
                        {
                            var metaOutputPath = Path.Combine(outputPath, ".meta");
                            CLIUtils.Log($"Adding {metaInputPath} to the transfer list.");
                            list.Add(new TransferFile(metaInputPath, metaOutputPath));
                        }
                        else
                        {
                            CLIUtils.Log("Meta file will be ignored");
                        }
                    }
                    else
                    {
                        CLIUtils.Log($"Found no meta file for {f}");
                    }
                });
            }

            CLIUtils.Log($"Transfer list contains {list.Count} items");
            var success = 0;
            var failed  = 0;
            var skipped = 0;

            list.Do(f =>
            {
                if (File.Exists(f.Output))
                {
                    if (Overwrite)
                    {
                        CLIUtils.Log($"Output file {f.Output} already exists, it will be overwritten");
                        if (f.IsMeta || Move)
                        {
                            CLIUtils.Log($"Deleting file at {f.Output}");
                            try
                            {
                                File.Delete(f.Output);
                            }
                            catch (Exception e)
                            {
                                CLIUtils.Log($"Could not delete file {f.Output}!\n{e}");
                                failed++;
                            }
                        }
                    }
                    else
                    {
                        CLIUtils.Log($"Output file {f.Output} already exists, skipping");
                        skipped++;
                        return;
                    }
                }

                if (f.IsMeta)
                {
                    CLIUtils.Log($"Writing meta data to {f.Output}");
                    try
                    {
                        File.WriteAllText(f.Output, f.Input, Encoding.UTF8);
                        success++;
                    }
                    catch (Exception e)
                    {
                        CLIUtils.Log($"Error while writing meta data to {f.Output}!\n{e}");
                        failed++;
                    }
                }
                else
                {
                    if (Copy)
                    {
                        CLIUtils.Log($"Copying file {f.Input} to {f.Output}");
                        try
                        {
                            File.Copy(f.Input, f.Output, Overwrite ? CopyOptions.None : CopyOptions.FailIfExists, CopyMoveProgressHandler, null);
                            success++;
                        }
                        catch (Exception e)
                        {
                            CLIUtils.Log($"Error while copying file {f.Input} to {f.Output}!\n{e}");
                            failed++;
                        }
                    }
                    else if (Move)
                    {
                        CLIUtils.Log($"Moving file {f.Input} to {f.Output}");
                        try
                        {
                            File.Move(f.Input, f.Output, Overwrite ? MoveOptions.ReplaceExisting : MoveOptions.None, CopyMoveProgressHandler, null);
                            success++;
                        }
                        catch (Exception e)
                        {
                            CLIUtils.Log($"Error while moving file {f.Input} to {f.Output}!\n{e}");
                            failed++;
                        }
                    }
                }
            });

            CLIUtils.Log($"Skipped transfers: {skipped}");
            CLIUtils.Log($"Failed transfers: {failed}");
            CLIUtils.Log($"Successful transfers: {success}");

            return(0);
        }
Exemple #16
0
        protected override async Task <ExitCode> Run()
        {
            var originalPath = (AbsolutePath)Original;
            var updatePath   = (AbsolutePath)Update;

            if (Original == null)
            {
                return(ExitCode.BadArguments);
            }
            if (Update == null)
            {
                return(ExitCode.BadArguments);
            }

            ModList original, update;

            try
            {
                original = AInstaller.LoadFromFile(originalPath);
            }
            catch (Exception e)
            {
                return(CLIUtils.Exit($"Error while loading the original Modlist from {Original}!\n{e}", ExitCode.Error));
            }

            if (original == null)
            {
                return(CLIUtils.Exit($"The Modlist from {Original} could not be loaded!", ExitCode.Error));
            }

            try
            {
                update = AInstaller.LoadFromFile(updatePath);
            }
            catch (Exception e)
            {
                return(CLIUtils.Exit($"Error while loading the updated Modlist from {Update}!\n{e}", ExitCode.Error));
            }

            if (update == null)
            {
                return(CLIUtils.Exit($"The Modlist from {Update} could not be loaded!", ExitCode.Error));
            }

            var downloadSizeChanges = original.DownloadSize - update.DownloadSize;
            var installSizeChanges  = original.InstallSize - update.InstallSize;

            var mdText =
                $"## {update.Version}\n\n" +
                $"**Build at:** `{File.GetCreationTime(Update)}`\n\n" +
                "**Info**:\n\n" +
                $"- Download Size change: {downloadSizeChanges.ToFileSizeString()} (Total: {update.DownloadSize.ToFileSizeString()})\n" +
                $"- Install Size change: {installSizeChanges.ToFileSizeString()} (Total: {update.InstallSize.ToFileSizeString()})\n\n";

            if (IncludeDownloadChanges)
            {
                var updatedArchives = update.Archives
                                      .Where(a => original.Archives.All(x => x.Name != a.Name))
                                      .Where(a =>
                {
                    if (!(a.State is NexusDownloader.State nexusState))
                    {
                        return(false);
                    }

                    return(original.Archives.Any(x =>
                    {
                        if (!(x.State is NexusDownloader.State originalState))
                        {
                            return false;
                        }

                        if (nexusState.Name != originalState.Name)
                        {
                            return false;
                        }

                        if (nexusState.ModID != originalState.ModID)
                        {
                            return false;
                        }


                        return nexusState.FileID > originalState.FileID;
                    }));
                }).ToList();

                var newArchives = update.Archives
                                  .Where(a => original.Archives.All(x => x.Name != a.Name))
                                  .Where(a => updatedArchives.All(x => x != a))
                                  .ToList();

                var removedArchives = original.Archives
                                      .Where(a => update.Archives.All(x => x.Name != a.Name))
                                      .Where(a => updatedArchives.All(x => x != a))
                                      .ToList();

                if (newArchives.Any() || removedArchives.Any())
                {
                    mdText += "**Download Changes**:\n\n";
                }

                updatedArchives.Do(a =>
                {
                    mdText += $"- Updated [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
                });

                removedArchives.Do(a =>
                {
                    mdText += $"- Removed [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
                });

                newArchives.Do(a =>
                {
                    mdText += $"- Added [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
                });

                mdText += "\n";
            }

            if (IncludeLoadOrderChanges)
            {
                var loadorderTxt          = (RelativePath)"loadorder.txt";
                var originalLoadOrderFile = original.Directives
                                            .Where(d => d is InlineFile)
                                            .Where(d => d.To.FileName == loadorderTxt)
                                            .Cast <InlineFile>()
                                            .First();

                var updatedLoadOrderFile = update.Directives
                                           .Where(d => d is InlineFile)
                                           .Where(d => d.To.FileName == loadorderTxt)
                                           .Cast <InlineFile>()
                                           .First();

                var originalLoadOrder = GetTextFileFromModlist(originalPath, original, originalLoadOrderFile.SourceDataID).Result.Split("\n");
                var updatedLoadOrder  = GetTextFileFromModlist(updatePath, update, updatedLoadOrderFile.SourceDataID).Result.Split("\n");

                var addedPlugins = updatedLoadOrder
                                   .Where(p => originalLoadOrder.All(x => p != x))
                                   .ToList();

                var removedPlugins = originalLoadOrder
                                     .Where(p => updatedLoadOrder.All(x => p != x))
                                     .ToList();

                if (addedPlugins.Any() || removedPlugins.Any())
                {
                    mdText += "**Load Order Changes**:\n\n";
                }

                addedPlugins.Do(p =>
                {
                    mdText += $"- Added {p}\n";
                });

                removedPlugins.Do(p =>
                {
                    mdText += $"- Removed {p}\n";
                });

                mdText += "\n";
            }

            if (IncludeModChanges)
            {
                var modlistTxt          = (RelativePath)"modlist.txt";
                var originalModlistFile = original.Directives
                                          .Where(d => d is InlineFile)
                                          .Where(d => d.To.FileName == modlistTxt)
                                          .Cast <InlineFile>()
                                          .First();

                var updatedModlistFile = update.Directives
                                         .Where(d => d is InlineFile)
                                         .Where(d => d.To.FileName == modlistTxt)
                                         .Cast <InlineFile>()
                                         .First();

                var originalModlist = GetTextFileFromModlist(originalPath, original, originalModlistFile.SourceDataID).Result.Split("\n");
                var updatedModlist  = GetTextFileFromModlist(updatePath, update, updatedModlistFile.SourceDataID).Result.Split("\n");

                var removedMods = originalModlist
                                  .Where(m => m.StartsWith("+"))
                                  .Where(m => updatedModlist.All(x => m != x))
                                  .Select(m => m.Substring(1))
                                  .ToList();

                var addedMods = updatedModlist
                                .Where(m => m.StartsWith("+"))
                                .Where(m => originalModlist.All(x => m != x))
                                .Select(m => m.Substring(1))
                                .ToList();

                if (removedMods.Any() || addedMods.Any())
                {
                    mdText += "**Mod Changes**:\n\n";
                }

                addedMods.Do(m =>
                {
                    mdText += $"- Added {m}\n";
                });

                removedMods.Do(m =>
                {
                    mdText += $"- Removed {m}\n";
                });
            }

            var output = string.IsNullOrWhiteSpace(Output)
                ? "changelog.md"
                : Output;

            if (File.Exists(output) && output.EndsWith("md"))
            {
                CLIUtils.Log($"Output file {output} already exists and is a markdown file. It will be updated with the newest version");

                var markdown = File.ReadAllLines(output).ToList();
                var lines    = mdText.Split("\n");

                if (lines.All(l => markdown.Contains(l)))
                {
                    return(CLIUtils.Exit("The output file is already up-to-date", ExitCode.Ok));
                }

                var doc = Markdown.Parse(File.ReadAllText(output));

                var hasToc  = false;
                var tocLine = 0;

                var headers = doc
                              .Where(b => b is HeadingBlock)
                              .Cast <HeadingBlock>()
                              .ToList();

                if (headers.Count < 2)
                {
                    return(CLIUtils.Exit("The provided output file has less than 2 headers!", ExitCode.Error));
                }

                if (headers[0].Level == 1 && headers[1].Level == 2)
                {
                    if (headers[1].Line - headers[0].Line > headers.Count - 1)
                    {
                        var listBlocks = doc
                                         .Where(b => b.Line > headers[0].Line && b.Line < headers[1].Line)
                                         .OfType <ListBlock>()
                                         .ToList();

                        if (listBlocks.Count == 1)
                        {
                            hasToc  = true;
                            tocLine = listBlocks[0].Line;

                            CLIUtils.Log($"Toc found at {tocLine}");
                        }
                    }
                }

                var firstHeader = headers
                                  .First(h => h.Level >= 2);

                var line = firstHeader.Line - 1;

                if (hasToc)
                {
                    markdown.Insert(tocLine, $"- [{update.Version}](#{ToTocLink(update.Version.ToString())})");
                    line++;
                }

                markdown.InsertRange(line + 1, lines);

                File.WriteAllLines(output, markdown);
                CLIUtils.Log($"Wrote {markdown.Count} lines to {output}");

                return(ExitCode.Ok);
            }

            var text = "# Changelog\n\n" +
                       $"- [{update.Version}](#{ToTocLink(update.Version.ToString())})\n\n" +
                       $"{mdText}";

            File.WriteAllText(output, text);
            CLIUtils.Log($"Wrote changelog to {output}");

            return(ExitCode.Ok);
        }
Exemple #17
0
        private static async Task ValidateList(DBContext db, ModlistMetadata list, WorkQueue queue, ValidateModlist whitelists)
        {
            var existing = await db.ModListStatus.FindOneAsync(l => l.Id == list.Links.MachineURL);

            var modlist_path = Path.Combine(Consts.ModListDownloadFolder, list.Links.MachineURL + Consts.ModListExtension);

            if (list.NeedsDownload(modlist_path))
            {
                if (File.Exists(modlist_path))
                {
                    File.Delete(modlist_path);
                }

                var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                Utils.Log($"Downloading {list.Links.MachineURL} - {list.Title}");
                await state.Download(modlist_path);
            }
            else
            {
                Utils.Log($"No changes detected from downloaded modlist");
            }


            Utils.Log($"Loading {modlist_path}");

            var installer = AInstaller.LoadFromFile(modlist_path);

            Utils.Log($"{installer.Archives.Count} archives to validate");

            DownloadDispatcher.PrepareAll(installer.Archives.Select(a => a.State));


            var validated = (await installer.Archives
                             .PMap(queue, async archive =>
            {
                bool is_failed;
                try
                {
                    is_failed = !(await archive.State.Verify(archive)) || !archive.State.IsWhitelisted(whitelists.ServerWhitelist);
                }
                catch (Exception)
                {
                    is_failed = false;
                }

                return(new DetailedStatusItem {
                    IsFailing = is_failed, Archive = archive
                });
            }))
                            .ToList();


            var status = new DetailedStatus
            {
                Name             = list.Title,
                Archives         = validated.OrderBy(v => v.Archive.Name).ToList(),
                DownloadMetaData = list.DownloadMetadata,
                HasFailures      = validated.Any(v => v.IsFailing)
            };

            var dto = new ModListStatus
            {
                Id      = list.Links.MachineURL,
                Summary = new ModlistSummary
                {
                    Name    = status.Name,
                    Checked = status.Checked,
                    Failed  = status.Archives.Count(a => a.IsFailing),
                    Passed  = status.Archives.Count(a => !a.IsFailing),
                },
                DetailedStatus = status,
                Metadata       = list
            };

            Utils.Log(
                $"Writing Update for {dto.Summary.Name} - {dto.Summary.Failed} failed - {dto.Summary.Passed} passed");
            await ModListStatus.Update(db, dto);

            Utils.Log(
                $"Done updating {dto.Summary.Name}");
        }
Exemple #18
0
        public ModListVM(string modListPath)
        {
            ModListPath = modListPath;
            try
            {
                SourceModList = AInstaller.LoadFromFile(modListPath);
            }
            catch (Exception ex)
            {
                Error = ex;
            }

            ImageObservable = Observable.Return(Unit.Default)
                              // Download and retrieve bytes on background thread
                              .ObserveOn(RxApp.TaskpoolScheduler)
                              .Select(filePath =>
            {
                try
                {
                    using (var fs = new FileStream(ModListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                        using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
                        {
                            var ms    = new MemoryStream();
                            var entry = ar.GetEntry("modlist-image.png");
                            if (entry == null)
                            {
                                return(default(MemoryStream));
                            }
                            using (var e = entry.Open())
                            {
                                e.CopyTo(ms);
                            }
                            return(ms);
                        }
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(MemoryStream));
                }
            })
                              // Create Bitmap image on GUI thread
                              .ObserveOnGuiThread()
                              .Select(memStream =>
            {
                if (memStream == null)
                {
                    return(default(BitmapImage));
                }
                try
                {
                    return(UIUtils.BitmapImageFromStream(memStream));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex, $"Exception while caching Mod List image {Name}");
                    return(default(BitmapImage));
                }
            })
                              // If ever would return null, show WJ logo instead
                              .Select(x =>
            {
                return(x ?? InstallerVM.WabbajackLogo);
            })
                              .Replay(1)
                              .RefCount();
        }