From 3883eddd5d7ed1cd2b657bacae31ee94782e227e Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sun, 26 Jan 2025 12:11:40 -0600 Subject: [PATCH] feat: memory --- src/CLI/Functions/Unpacker.cs | 48 ++++++------- src/CLI/Utils/ExplicitlySizedStream.cs | 95 ++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 src/CLI/Utils/ExplicitlySizedStream.cs diff --git a/src/CLI/Functions/Unpacker.cs b/src/CLI/Functions/Unpacker.cs index 480e86110..d4fb18d89 100644 --- a/src/CLI/Functions/Unpacker.cs +++ b/src/CLI/Functions/Unpacker.cs @@ -42,31 +42,28 @@ namespace CLI.Functions throw new Exception("Failed to read content build id from DefaultDlc.ini"); } - Console.WriteLine($"Exploring DLC {contentBuildId} within group {CONTENT_GROUP}"); + Console.WriteLine($"Exploring DLC \"{contentBuildId}\" within group \"{CONTENT_GROUP}\""); using HttpClient httpClient = new(); DlcManifest manifest = await FetchManifest(CONTENT_GROUP, contentBuildId); foreach (var pakFile in manifest.PakFiles) { - Console.WriteLine($"{pakFile.FileName} downloading exergy..."); + Console.WriteLine($"Exergizing DLC container \"{pakFile.FileName}\""); string url = $"{WG_DLC_DOMAIN}/dlc/{CONTENT_GROUP}/dlc/{pakFile.RelativeUrl}"; // load to stream - Stream stream = await httpClient.GetStreamAsync(url); - FStreamArchive archive = new(url, stream); + Stream httpStream = await httpClient.GetStreamAsync(url); + MemoryStream memoryStream = new(); + + await httpStream.CopyToAsync(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + + FStreamArchive archive = new(url, memoryStream); PakFileReader reader = new(archive); - reader.Mount(); - - foreach (var file in reader.Files) - { - var gameFile = file.Value; - Console.WriteLine(gameFile.Path); - } - - break; + Exergize(reader); } } @@ -90,22 +87,21 @@ namespace CLI.Functions foreach (string container in containers) { - Exergy(container); + Console.WriteLine($"Exergizing disk container \"{container}\""); + + PakFileReader reader = new(container); + Exergize(reader); } if (Directory.Exists(TEMP_DEPOT_DIR)) { - Console.WriteLine("Deleting temp depots directory..."); + Console.WriteLine("Deleting temp depots directory"); Directory.Delete(TEMP_DEPOT_DIR, true); } } - private static void Exergy(string container) + private static void Exergize(PakFileReader reader) { - Console.WriteLine($"Filtering {container}"); - - PakFileReader reader = new(container); - reader.Mount(); foreach (var file in reader.Files) @@ -115,6 +111,7 @@ namespace CLI.Functions if (!exergizable) { + Console.WriteLine($"Exergize 🔴 {gameFile.Path}"); continue; } @@ -122,11 +119,13 @@ namespace CLI.Functions if (File.Exists(writePath)) { - Console.WriteLine($"{gameFile.Path} already exists; skipping..."); + Console.WriteLine($"Exergize 🟡 {gameFile.Path}"); continue; } - - Console.WriteLine($"{gameFile.Path} writing from vfs..."); + else + { + Console.WriteLine($"Exergize 🟢 {gameFile.Path}"); + } string? directory = Path.GetDirectoryName(writePath); @@ -143,7 +142,8 @@ namespace CLI.Functions private static bool ShouldExergize(string path) { - return ExergizableFiles.Contains(path); + return true; + // return ExergizableFiles.Contains(path); } } } diff --git a/src/CLI/Utils/ExplicitlySizedStream.cs b/src/CLI/Utils/ExplicitlySizedStream.cs new file mode 100644 index 000000000..4940285ea --- /dev/null +++ b/src/CLI/Utils/ExplicitlySizedStream.cs @@ -0,0 +1,95 @@ +namespace CLI.Utils +{ + public class ExplicitlySizedStream(Stream stream, long length) : Stream + { + private readonly Stream _stream = stream; + private readonly long _length = length; + private long _position = 0; + + public override bool CanRead => _stream.CanRead; + public override bool CanSeek => _stream.CanSeek; + public override bool CanWrite => _stream.CanWrite; + + public override long Length => _length; + public override long Position + { + get => _position; + set => _position = value; + } + + public override void Flush() => _stream.Flush(); + + public override int Read(byte[] buffer, int offset, int count) + { + if (_position >= _length) + return 0; // End of stream + var bytesRead = _stream.Read(buffer, offset, count); + _position += bytesRead; + return bytesRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + long targetPosition = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.Current => _position + offset, + SeekOrigin.End => _length + offset, + _ => throw new ArgumentOutOfRangeException(nameof(origin), "Invalid SeekOrigin value."), + }; + + if (targetPosition < 0 || targetPosition > _length) + throw new ArgumentOutOfRangeException(nameof(offset), "Seek position is out of range."); + + // Simulate seek by discarding data up to the target position + if (targetPosition != _position) + { + if (targetPosition < _position) + { + throw new NotSupportedException( + "Backward seeking is not supported for non-seekable streams." + ); + } + + var toSkip = targetPosition - _position; + Skip(toSkip); + _position = targetPosition; + } + + return _position; + } + + private void Skip(long bytes) + { + byte[] buffer = new byte[8192]; + while (bytes > 0) + { + int toRead = (int)Math.Min(buffer.Length, bytes); + int read = _stream.Read(buffer, 0, toRead); + if (read == 0) + throw new EndOfStreamException(); + bytes -= read; + } + } + + public override void SetLength(long value) => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + _position += count; + } + + // public override void Flush() => _stream.Flush(); + + // public override int Read(byte[] buffer, int offset, int count) => + // _stream.Read(buffer, offset, count); + + // public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin); + + // public override void SetLength(long value) => throw new NotSupportedException(); + + // public override void Write(byte[] buffer, int offset, int count) => + // _stream.Write(buffer, offset, count); + } +}