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;
+ }
+ }
+}