From 53e27d088d203960387496fbbe89f9a610d415f3 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 31 Aug 2023 02:32:04 +0200 Subject: [PATCH] Improve info and extract commands - You can now choose to either specify regions to download or regions to skip - You can now display more than 4096 files by using the '-a' ('--show-all-files') option with the info command. --- XvdTool.Streaming/Program.cs | 24 ++++++++++-- XvdTool.Streaming/StreamedXvdFile.cs | 56 +++++++++++++++++++++------- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/XvdTool.Streaming/Program.cs b/XvdTool.Streaming/Program.cs index 013443d..6bd8500 100644 --- a/XvdTool.Streaming/Program.cs +++ b/XvdTool.Streaming/Program.cs @@ -150,6 +150,10 @@ public sealed class Settings : XvdCommandSettings [Description("File path to save the output into.")] [CommandOption("-o|--output")] public string? OutputPath { get; init; } + + [Description("If all files should be printed.\nIf unset, only the first 4096 files will be printed.")] + [CommandOption(("-a|--show-all-files"))] + public bool ShowAllFiles { get; set; } } public override int Execute(CommandContext context, Settings settings) @@ -160,7 +164,7 @@ public override int Execute(CommandContext context, Settings settings) using (XvdFile) { - var infoOutput = XvdFile.PrintInfo(); + var infoOutput = XvdFile.PrintInfo(settings.ShowAllFiles); if (settings.OutputPath != null) { var directory = Path.GetDirectoryName(settings.OutputPath); @@ -185,8 +189,12 @@ public sealed class Settings : CryptoCommandSettings public string? OutputDirectory { get; init; } [Description("List of regions to skip downloading. Defaults to none.")] - [CommandOption("-r|--skip-region")] - public uint[]? SkippedRegions { get; init; } + [CommandOption("-b|--skip-region")] + public uint[]? SkipRegions { get; init; } + + [Description("List of regions to download. Defaults to all.")] + [CommandOption("-w|--download-region")] + public uint[]? DownloadRegions { get; init; } [Description("Skips performing hash verification on the pages prior to decryption.\nMassively improves performance at the cost of integrity.\nOnly use this if you know the file is not corrupt!")] [CommandOption("-n|--no-hash-check")] @@ -210,13 +218,21 @@ public override int Execute(CommandContext context, Settings settings) using (XvdFile) { - XvdFile.ExtractFiles(outputPath, keyEntry, settings.SkipHashCheck, settings.SkippedRegions); + XvdFile.ExtractFiles(outputPath, keyEntry, settings.SkipHashCheck, settings.SkipRegions, settings.DownloadRegions); } ConsoleLogger.WriteInfoLine("[green bold]Successfully[/] extracted files."); return 0; } + + public override ValidationResult Validate(CommandContext context, Settings settings) + { + if (settings is {DownloadRegions: not null, SkipRegions: not null}) + return ValidationResult.Error("'--skip-region' and '--download-region' cannot be used together."); + + return ValidationResult.Success(); + } } internal sealed class VerifyCommand : XvdCommand diff --git a/XvdTool.Streaming/StreamedXvdFile.cs b/XvdTool.Streaming/StreamedXvdFile.cs index f810553..c8535be 100644 --- a/XvdTool.Streaming/StreamedXvdFile.cs +++ b/XvdTool.Streaming/StreamedXvdFile.cs @@ -302,7 +302,7 @@ public bool VerifyDataHashes() return LocalVerifyDataHashes(); } - public void ExtractFiles(string outputPath, in KeyEntry key, bool skipHashCheck = false, uint[]? skippedRegionList = null) + public void ExtractFiles(string outputPath, in KeyEntry key, bool skipHashCheck = false, uint[]? skippedRegionList = null, uint[]? downloadRegionList = null) { if (!_hasXvcInfo) { @@ -340,12 +340,24 @@ public void ExtractFiles(string outputPath, in KeyEntry key, bool skipHashCheck var firstSegmentOffset = XvdMath.PageNumberToOffset(_xvcUpdateSegments[0].PageNum); - var extractableRegionList = - _xvcRegions - .Where(region => - (skippedRegionList == null || !skippedRegionList.Contains((uint)region.Id)) + XvcRegionHeader[] extractableRegionList; + + if (downloadRegionList != null) + { + extractableRegionList = _xvcRegions + .Where(region => downloadRegionList.Contains((uint)region.Id) && (region.FirstSegmentIndex != 0 || firstSegmentOffset == region.Offset)) - .ToArray(); + .ToArray(); + } + else + { + extractableRegionList = + _xvcRegions + .Where(region => + (skippedRegionList == null || !skippedRegionList.Contains((uint)region.Id)) + && (region.FirstSegmentIndex != 0 || firstSegmentOffset == region.Offset)) + .ToArray(); + } // Fancy console version AnsiConsole.Progress() @@ -553,7 +565,7 @@ private void ExtractRegion( } } - public string PrintInfo() + public string PrintInfo(bool showAllFiles = false) { AnsiConsole.Record(); @@ -665,6 +677,7 @@ static Rows StringToRows(string text) .AddColumns( new TableColumn("Offset"), new TableColumn("Size"), + new TableColumn("Size in Bytes"), new TableColumn("File Path") ) .RoundedBorder() @@ -675,6 +688,7 @@ static Rows StringToRows(string text) packageFilesTable.AddRow( new Markup($"[green bold]0x{entry.Offset:x8}[/]"), new Markup($"[green bold]0x{entry.Size:x8}[/]"), + new Markup($"[green bold]{ToFileSize(entry.Size)}[/]"), new Markup($"[aqua underline]{entry.FilePath}[/]") ); } @@ -701,6 +715,7 @@ static Rows StringToRows(string text) .AddColumns( new TableColumn("Start Page"), new TableColumn("File Size"), + new TableColumn("Size in Bytes"), new TableColumn("Hash"), new TableColumn("Flags"), new TableColumn("File Path") @@ -708,7 +723,7 @@ static Rows StringToRows(string text) .RoundedBorder() .Expand(); - for (int i = 0; i < Math.Min(_segments.Length, 0x1000); i++) + for (int i = 0; i < (showAllFiles ? _segments.Length : Math.Min(_segments.Length, 0x1000)); i++) { var segment = _segments[i]; var updateSegment = _xvcUpdateSegments[i]; @@ -716,16 +731,17 @@ static Rows StringToRows(string text) segmentTable.AddRow( new Markup($"[green]0x{updateSegment.PageNum:x8}[/]"), new Markup($"[green]0x{segment.FileSize:x16}[/]"), + new Markup($"[green]{ToFileSize(segment.FileSize)}[/]"), new Markup($"[green]0x{updateSegment.Hash:x16}[/]"), new Markup(segment.Flags != 0x0 ? $"[fuchsia]0x{(byte)segment.Flags:x2}[/]" : $"0x{segment.Flags}"), new Markup($"[aqua underline]{_segmentPaths[i]}[/]") ); + } - if (_segments.Length > 0x1000) - { - segmentTable.AddEmptyRow(); - segmentTable.AddRow(new Markup("[red bold][/]")); - } + if (!showAllFiles && _segments.Length > 0x1000) + { + segmentTable.AddEmptyRow(); + segmentTable.AddRow(new Markup("[red bold][/]")); } AnsiConsole.Write(segmentTable); @@ -733,6 +749,20 @@ static Rows StringToRows(string text) } return AnsiConsole.ExportText(); + + string ToFileSize(ulong size) + { + if (size < 1024) + return $"{size} B"; + + if (size < 1024 * 1024) + return $"{Math.Round(size / 1024.0, 2)} KB"; + + if (size < 1024 * 1024 * 1024) + return $"{Math.Round(size / (1024.0 * 1024.0), 2)} MB"; + + return $"{Math.Round(size / (1024.0 * 1024.0 * 1024.0), 2)} KB"; + } } public void Dispose()