diff --git a/OpenRA.Game/FileSystem/MixFile.cs b/OpenRA.Game/FileSystem/MixFile.cs index f1da7bef18..31d8c9341e 100644 --- a/OpenRA.Game/FileSystem/MixFile.cs +++ b/OpenRA.Game/FileSystem/MixFile.cs @@ -14,6 +14,7 @@ using System.Globalization; using System.IO; using System.Linq; using OpenRA.FileFormats; +using OpenRA.Primitives; namespace OpenRA.FileSystem { @@ -179,9 +180,10 @@ namespace OpenRA.FileSystem if (!index.TryGetValue(hash, out e)) return null; - s.Seek(dataStart + e.Offset, SeekOrigin.Begin); - var data = s.ReadBytes((int)e.Length); - return new MemoryStream(data); + Stream parentStream; + var offset = dataStart + e.Offset + SegmentStream.GetOverallNestedOffset(s, out parentStream); + var path = ((FileStream)parentStream).Name; + return new SegmentStream(File.OpenRead(path), offset, e.Length); } public Stream GetContent(string filename) diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index fd235f027a..e937eb0d16 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -150,6 +150,7 @@ + diff --git a/OpenRA.Game/Primitives/SegmentStream.cs b/OpenRA.Game/Primitives/SegmentStream.cs new file mode 100644 index 0000000000..3877e762a5 --- /dev/null +++ b/OpenRA.Game/Primitives/SegmentStream.cs @@ -0,0 +1,102 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.ComponentModel; +using System.IO; + +namespace OpenRA.Primitives +{ + public class SegmentStream : Stream + { + public readonly Stream BaseStream; + public readonly long BaseOffset; + public readonly long BaseCount; + + public SegmentStream(Stream stream, long offset, long count) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanSeek) + throw new ArgumentException("stream must be seekable.", "stream"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "offset must be non-negative."); + if (count < 0) + throw new ArgumentOutOfRangeException("count", "count must be non-negative."); + + BaseStream = stream; + BaseOffset = offset; + BaseCount = count; + + stream.Seek(BaseOffset, SeekOrigin.Begin); + } + + public override bool CanSeek { get { return true; } } + public override bool CanRead { get { return BaseStream.CanRead; } } + public override bool CanWrite { get { return BaseStream.CanWrite; } } + + public override long Length { get { return BaseCount; } } + public override long Position + { + get { return BaseStream.Position - BaseOffset; } + set { BaseStream.Position = BaseOffset + value; } + } + + public override int Read(byte[] buffer, int offset, int count) { return BaseStream.Read(buffer, offset, count); } + public override void Write(byte[] buffer, int offset, int count) { BaseStream.Write(buffer, offset, count); } + public override void Flush() { BaseStream.Flush(); } + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + default: throw new InvalidEnumArgumentException("origin", (int)origin, typeof(SeekOrigin)); + case SeekOrigin.Begin: + return BaseStream.Seek(BaseOffset + offset, SeekOrigin.Begin) - offset; + case SeekOrigin.Current: + return BaseStream.Seek(offset, SeekOrigin.Current) - offset; + case SeekOrigin.End: + return BaseStream.Seek(offset, SeekOrigin.End) - offset; + } + } + + public override void SetLength(long value) { throw new NotSupportedException(); } + + public override bool CanTimeout { get { return BaseStream.CanTimeout; } } + + public override int ReadTimeout + { + get { return BaseStream.ReadTimeout; } + set { BaseStream.ReadTimeout = value; } + } + + public override int WriteTimeout + { + get { return BaseStream.WriteTimeout; } + set { BaseStream.WriteTimeout = value; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + BaseStream.Dispose(); + base.Dispose(disposing); + } + + public static long GetOverallNestedOffset(Stream stream, out Stream overallBaseStream) + { + var offset = 0L; + overallBaseStream = stream; + var segmentStream = stream as SegmentStream; + if (segmentStream != null) + offset += segmentStream.BaseOffset + GetOverallNestedOffset(segmentStream.BaseStream, out overallBaseStream); + return offset; + } + } +}