/// <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)); }
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(); }
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(); }
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(); }
/// <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); }
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(); }
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}"); }
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); }
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); }
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}"); }
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(); }