diff --git a/OpenRA.Mods.Common/FileFormats/MSCabCompression.cs b/OpenRA.Mods.Common/FileFormats/MSCabCompression.cs index 998b8e9992..ba73834d4a 100644 --- a/OpenRA.Mods.Common/FileFormats/MSCabCompression.cs +++ b/OpenRA.Mods.Common/FileFormats/MSCabCompression.cs @@ -87,42 +87,44 @@ namespace OpenRA.Mods.Common.FileFormats files[i] = new CabFile(stream); } - public byte[] ExtractFile(string filename, Action onProgress = null) + public void ExtractFile(string filename, Stream output, Action onProgress = null) { var file = files.FirstOrDefault(f => f.FileName == filename); if (file == null) - return null; + throw new FileNotFoundException(filename); var folder = folders[file.FolderIndex]; stream.Seek(folder.BlockOffset, SeekOrigin.Begin); - using (var outputStream = new MemoryStream()) + var inflater = new Inflater(true); + var buffer = new byte[4096]; + var decompressedBytes = 0; + for (var i = 0; i < folder.BlockCount; i++) { - var inflater = new Inflater(true); - var buffer = new byte[4096]; - for (var i = 0; i < folder.BlockCount; i++) + if (onProgress != null) + onProgress((int)(100 * output.Position / file.DecompressedLength)); + + // Ignore checksums + stream.Position += 4; + var blockLength = stream.ReadUInt16(); + stream.Position += 4; + + using (var batch = new MemoryStream(stream.ReadBytes(blockLength - 2))) + using (var inflaterStream = new InflaterInputStream(batch, inflater)) { - if (onProgress != null) - onProgress((int)(100 * outputStream.Position / file.DecompressedLength)); - - // Ignore checksums - stream.Position += 4; - var blockLength = stream.ReadUInt16(); - stream.Position += 4; - - using (var batch = new MemoryStream(stream.ReadBytes(blockLength - 2))) - using (var inflaterStream = new InflaterInputStream(batch, inflater)) + int n; + while ((n = inflaterStream.Read(buffer, 0, buffer.Length)) > 0) { - int n; - while ((n = inflaterStream.Read(buffer, 0, buffer.Length)) > 0) - outputStream.Write(buffer, 0, n); - } + var offset = Math.Max(0, file.DecompressedOffset - decompressedBytes); + var count = Math.Min(n - offset, file.DecompressedLength - decompressedBytes); + if (offset < n) + output.Write(buffer, (int)offset, (int)count); - inflater.Reset(); + decompressedBytes += n; + } } - outputStream.Seek(file.DecompressedOffset, SeekOrigin.Begin); - return outputStream.ReadBytes((int)file.DecompressedLength); + inflater.Reset(); } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromDiscLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromDiscLogic.cs index dbd28ceacb..540cc629a9 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromDiscLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromDiscLogic.cs @@ -304,12 +304,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); using (var target = File.OpenWrite(targetPath)) { - // This is a bit dumb memory-wise, but we load the whole thing when running the game anyway Log.Write("install", "Extracting {0} -> {1}".F(sourcePath, targetPath)); - var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); Action onProgress = percent => updateMessage("Extracting {0} ({1}%)".F(displayFilename, percent)); - target.Write(reader.ExtractFile(node.Value.Value, onProgress)); + reader.ExtractFile(node.Value.Value, target, onProgress); } } }