using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.CommandLine.NamingConventionBinder; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Nettle; using Wabbajack.CLI.Builder; using Wabbajack.Common; using Wabbajack.DTOs.Directives; using Wabbajack.DTOs.JsonConverters; using Wabbajack.Installer; using Wabbajack.Paths; using Wabbajack.Paths.IO; namespace Wabbajack.CLI.Verbs; public class ModlistReport { private readonly ILogger _logger; private readonly DTOSerializer _dtos; public ModlistReport(ILogger logger, DTOSerializer dtos) { _logger = logger; _dtos = dtos; } public static VerbDefinition Definition = new("modlist-report", "Generates a usage report for a Modlist file", new[] { new OptionDefinition(typeof(AbsolutePath), "i", "input", "Wabbajack file from which to generate a report") }); private static async Task ReportTemplate(object o) { var data = await (typeof(ModlistReport).Assembly.GetManifestResourceStream("Wabbajack.CLI.Resources.ModlistReport.html")!).ReadAllAsync(); var func = NettleEngine.GetCompiler().Compile(Encoding.UTF8.GetString(data)); return await func(o, CancellationToken.None); } public async Task Run(AbsolutePath input) { _logger.LogInformation("Loading Modlist"); var modlist = await StandardInstaller.LoadFromFile(_dtos, input); Dictionary patchSizes; using (var zip = new ZipArchive(input.Open(FileMode.Open, FileAccess.Read, FileShare.Read))) { patchSizes = zip.Entries.ToDictionary(e => e.Name, e => e.Length); } var archives = modlist.Archives.ToDictionary(a => a.Hash, a => a.Name); var bsas = modlist.Directives.OfType().ToDictionary(bsa => bsa.TempID.ToString()); var inlinedData = modlist.Directives.OfType() .Select(e => new { To = e.To.ToString(), Id = e.SourceDataID.ToString(), SizeInt = e.Size, Size = e.Size.ToFileSizeString() }).ToArray(); string FixupTo(RelativePath path) { if (path.GetPart(0) != Consts.BSACreationDir.ToString()) return path.ToString(); var bsaId = path.GetPart(1); if (!bsas.TryGetValue(bsaId, out var bsa)) { return path.ToString(); } var relPath = RelativePath.FromParts(path.Parts[2..]); return $" {bsa.To} | {relPath}"; } var patchData = modlist.Directives.OfType() .Select(e => new { From = $" {archives[e.ArchiveHashPath.Hash]} | {string.Join(" | ", e.ArchiveHashPath.Parts.Select(e => e.ToString()))}", To = FixupTo(e.To), Id = e.PatchID.ToString(), PatchSize = patchSizes[e.PatchID.ToString()].ToFileSizeString(), PatchSizeInt = patchSizes[e.PatchID.ToString()], FinalSize = e.Size.ToFileSizeString(), }).ToArray(); var data = await ReportTemplate(new { Name = modlist.Name, TotalInlinedSize = inlinedData.Sum(i => i.SizeInt).ToFileSizeString(), InlinedData = inlinedData, TotalPatchSize = patchData.Sum(i => i.PatchSizeInt).ToFileSizeString(), PatchData = patchData, WabbajackSize = input.Size().ToFileSizeString() }); await input.WithExtension(Ext.Html).WriteAllTextAsync(data); return 0; } }