Index: common/stream.h
===================================================================
--- common/stream.h	(revision 24488)
+++ common/stream.h	(working copy)
@@ -210,14 +210,14 @@
  * @todo We really need better error handling here!
  *       Like seek should somehow indicate whether it failed.
  */
-class SeekableReadStream : public ReadStream {
+class SeekableReadStream : virtual public ReadStream {
 public:
 
 	virtual uint32 pos() const = 0;
 	virtual uint32 size() const = 0;
 
 	virtual void seek(int32 offset, int whence = SEEK_SET) = 0;
-	
+
 	void skip(uint32 offset) { seek(offset, SEEK_CUR); }
 
 	/**
@@ -233,7 +233,44 @@
 	virtual char *readLine(char *buf, size_t bufSize);
 };
 
+/**
+ * SubReadStream provides access to a ReadStream restricted to the range
+ * [currentPosition, currentPosition+end).
+ * Manipulating the parent stream directly /will/ mess up a substream.
+ * Likewise, manipulating two substreams of a parent stream will cause them to
+ * step on each others toes.
+ */
+class SubReadStream : virtual public ReadStream {
+protected:
+	ReadStream *_parentStream;
+	uint32 _pos;
+	uint32 _end;
+public:
+	SubReadStream(ReadStream *parentStream, uint32 end)
+		: _parentStream(parentStream), _pos(0), _end(end) {}
 
+	virtual bool eos() const { return _pos == _end; }
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+};
+
+/*
+ * SeekableSubReadStream provides access to a SeekableReadStream restricted to
+ * the range [begin, end).
+ * The same caveats apply to SeekableSubReadStream as do to SeekableReadStream.
+ */
+class SeekableSubReadStream : public SubReadStream, public SeekableReadStream {
+protected:
+	SeekableReadStream *_parentStream;
+	uint32 _begin;
+public:
+	SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end);
+
+	virtual uint32 pos() const { return _pos - _begin; }
+	virtual uint32 size() const { return _end - _begin; }
+
+	virtual void seek(int32 offset, int whence = SEEK_SET);
+};
+
 /**
  * XORReadStream is a wrapper around an arbitrary other ReadStream,
  * which 'decrypts' the data being read by XORing all data bytes with the given
Index: common/stream.cpp
===================================================================
--- common/stream.cpp	(revision 24488)
+++ common/stream.cpp	(working copy)
@@ -105,7 +105,7 @@
 
 		c = readByte();
 	}
-	
+
 	// This should fix a bug while using readLine with Common::File
 	// it seems that it sets the eos flag after an invalid read
 	// and at the same time the ioFailed flag
@@ -118,5 +118,45 @@
 	return buf;
 }
 
+uint32 SubReadStream::read(void *dataPtr, uint32 dataSize)
+{
+	dataSize = MIN(dataSize, _end - _pos);
 
+	dataSize = _parentStream->read(dataPtr, dataSize);
+	_pos += dataSize;
+
+	return dataSize;
+}
+
+SeekableSubReadStream::SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end)
+	: SubReadStream(parentStream, end), _parentStream(parentStream), _begin(begin)
+{
+	assert(_begin <= _end);
+	_pos = _begin;
+	_parentStream->seek(_pos);
+}
+
+void SeekableSubReadStream::seek(int32 offset, int whence)
+{
+	assert(_pos >= _begin);
+	assert(_pos <= _end);
+
+	switch(whence)
+	{
+	case SEEK_END:
+		offset = size() - offset;
+		// fallthrough
+	case SEEK_SET:
+		_pos = _begin + offset;
+		break;
+	case SEEK_CUR:
+		_pos += offset;
+	}
+
+	assert(_pos >= _begin);
+	assert(_pos <= _end);
+
+	_parentStream->seek(_pos);
+}
+
 }	// End of namespace Common
Index: test/common/seekablesubreadstream.h
===================================================================
--- test/common/seekablesubreadstream.h	(revision 0)
+++ test/common/seekablesubreadstream.h	(revision 0)
@@ -0,0 +1,72 @@
+#include <cxxtest/TestSuite.h>
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+
+class SeekableSubReadStreamTestSuite : public CxxTest::TestSuite
+{
+	public:
+	void test_traverse( void )
+	{
+		byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10);
+
+		int start = 2, end = 8;
+
+		Common::SeekableSubReadStream ssrs = Common::SeekableSubReadStream(&ms, start, end);
+
+		int i;
+		byte b;
+		for (i = start; i < end; ++i)
+		{
+			TS_ASSERT( !ssrs.eos() );
+
+			TS_ASSERT_EQUALS( i - start, ssrs.pos() );
+
+			ssrs.read(&b, 1);
+			TS_ASSERT_EQUALS( i, b );
+		}
+
+		TS_ASSERT( ssrs.eos() );
+	}
+
+	void test_seek( void )
+	{
+		byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10);
+
+		Common::SeekableSubReadStream ssrs = Common::SeekableSubReadStream(&ms, 1, 9);
+		byte b;
+
+		TS_ASSERT_EQUALS( ssrs.pos(), 0 );
+
+		ssrs.seek(1, SEEK_SET);
+		TS_ASSERT_EQUALS( ssrs.pos(), 1 );
+		b = ssrs.readByte();
+		TS_ASSERT_EQUALS( b, 2 );
+
+		ssrs.seek(5, SEEK_CUR);
+		TS_ASSERT_EQUALS( ssrs.pos(), 7 );
+		b = ssrs.readByte();
+		TS_ASSERT_EQUALS( b, 8 );
+
+		ssrs.seek(-3, SEEK_CUR);
+		TS_ASSERT_EQUALS( ssrs.pos(), 5 );
+		b = ssrs.readByte();
+		TS_ASSERT_EQUALS( b, 6 );
+
+		ssrs.seek(0, SEEK_END);
+		TS_ASSERT_EQUALS( ssrs.pos(), 8 );
+		TS_ASSERT( ssrs.eos() );
+
+		ssrs.seek(3, SEEK_END);
+		TS_ASSERT_EQUALS( ssrs.pos(), 5 );
+		b = ssrs.readByte();
+		TS_ASSERT_EQUALS( b, 6 );
+
+		ssrs.seek(8, SEEK_END);
+		TS_ASSERT_EQUALS( ssrs.pos(), 0 );
+		b = ssrs.readByte();
+		TS_ASSERT_EQUALS( b, 1 );
+	}
+};
Index: test/common/subreadstream.h
===================================================================
--- test/common/subreadstream.h	(revision 0)
+++ test/common/subreadstream.h	(revision 0)
@@ -0,0 +1,30 @@
+#include <cxxtest/TestSuite.h>
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+
+class SubReadStreamTestSuite : public CxxTest::TestSuite
+{
+	public:
+	void test_traverse( void )
+	{
+		byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10);
+
+		int end = 5;
+
+		Common::SubReadStream srs = Common::SubReadStream(&ms, end);
+
+		int i;
+		byte b;
+		for (i = 0; i < end; ++i)
+		{
+			TS_ASSERT( !srs.eos() );
+
+			srs.read(&b, 1);
+			TS_ASSERT_EQUALS( i, b );
+		}
+
+		TS_ASSERT( srs.eos() );
+	}
+};
