mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 08:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "constrainedfiledatastream.hpp"
 | 
						|
#include "lowlevelfile.hpp"
 | 
						|
 | 
						|
#include <stdexcept>
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ConstrainedDataStream : public Ogre::DataStream {
 | 
						|
public:
 | 
						|
 | 
						|
	static const size_t sBufferSize = 4096; // somewhat arbitrary though 64KB buffers didn't seem to improve performance any
 | 
						|
	static const size_t sBufferThreshold = 1024; // reads larger than this bypass buffering as cost of memcpy outweighs cost of system call
 | 
						|
 | 
						|
    ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length)
 | 
						|
	{
 | 
						|
		mFile.open (fname.c_str ());
 | 
						|
		mSize  = length != 0xFFFFFFFF ? length : mFile.size () - start;
 | 
						|
 | 
						|
		mPos    = 0;
 | 
						|
		mOrigin = start;
 | 
						|
		mExtent = start + mSize;
 | 
						|
 | 
						|
		mBufferOrigin = 0;
 | 
						|
		mBufferExtent = 0;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	size_t read(void* buf, size_t count)
 | 
						|
	{
 | 
						|
		assert (mPos <= mSize);
 | 
						|
 | 
						|
		uint8_t * out = reinterpret_cast <uint8_t *> (buf);
 | 
						|
 | 
						|
		size_t posBeg = mOrigin + mPos;
 | 
						|
		size_t posEnd = posBeg + count;
 | 
						|
 | 
						|
		if (posEnd > mExtent)
 | 
						|
			posEnd = mExtent;
 | 
						|
 | 
						|
		size_t posCur = posBeg;
 | 
						|
 | 
						|
		while (posCur != posEnd)
 | 
						|
		{
 | 
						|
			size_t readLeft = posEnd - posCur;
 | 
						|
 | 
						|
			if (posCur < mBufferOrigin || posCur >= mBufferExtent)
 | 
						|
			{
 | 
						|
				if (readLeft >= sBufferThreshold || (posCur == mOrigin && posEnd == mExtent))
 | 
						|
				{
 | 
						|
					assert (mFile.tell () == mBufferExtent);
 | 
						|
 | 
						|
					if (posCur != mBufferExtent)
 | 
						|
						mFile.seek (posCur);
 | 
						|
 | 
						|
					posCur += mFile.read (out, readLeft);
 | 
						|
 | 
						|
					mBufferOrigin = mBufferExtent = posCur;
 | 
						|
 | 
						|
					mPos = posCur - mOrigin;
 | 
						|
 | 
						|
					return posCur - posBeg;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					size_t newBufferOrigin;
 | 
						|
 | 
						|
					if ((posCur < mBufferOrigin) && (mBufferOrigin - posCur < sBufferSize))
 | 
						|
						newBufferOrigin = std::max (mOrigin, mBufferOrigin > sBufferSize ? mBufferOrigin - sBufferSize : 0);
 | 
						|
					else
 | 
						|
						newBufferOrigin = posCur;
 | 
						|
 | 
						|
					fill (newBufferOrigin);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			size_t xfer = std::min (readLeft, mBufferExtent - posCur);
 | 
						|
 | 
						|
			memcpy (out, mBuffer + (posCur - mBufferOrigin), xfer);
 | 
						|
 | 
						|
			posCur += xfer;
 | 
						|
			out += xfer;
 | 
						|
		}
 | 
						|
 | 
						|
		count = posEnd - posBeg;
 | 
						|
		mPos += count;
 | 
						|
		return count;
 | 
						|
	}
 | 
						|
 | 
						|
    void skip(long count)
 | 
						|
    {
 | 
						|
		assert (mPos <= mSize);
 | 
						|
 | 
						|
        if((count >= 0 && (size_t)count <= mSize-mPos) ||
 | 
						|
           (count < 0 && (size_t)-count <= mPos))
 | 
						|
			mPos += count;
 | 
						|
    }
 | 
						|
 | 
						|
    void seek(size_t pos)
 | 
						|
    {
 | 
						|
		assert (mPos <= mSize);
 | 
						|
 | 
						|
        if (pos < mSize)
 | 
						|
			mPos = pos;
 | 
						|
    }
 | 
						|
 | 
						|
    virtual size_t tell() const
 | 
						|
    {
 | 
						|
		assert (mPos <= mSize);
 | 
						|
 | 
						|
		return mPos;
 | 
						|
	}
 | 
						|
 | 
						|
    virtual bool eof() const
 | 
						|
    {
 | 
						|
		assert (mPos <= mSize);
 | 
						|
 | 
						|
		return mPos == mSize;
 | 
						|
	}
 | 
						|
 | 
						|
    virtual void close()
 | 
						|
    {
 | 
						|
		mFile.close();
 | 
						|
	}
 | 
						|
 | 
						|
private:
 | 
						|
 | 
						|
	void fill (size_t newOrigin)
 | 
						|
	{
 | 
						|
		assert (mFile.tell () == mBufferExtent);
 | 
						|
 | 
						|
		size_t newExtent = newOrigin + sBufferSize;
 | 
						|
 | 
						|
		if (newExtent > mExtent)
 | 
						|
			newExtent = mExtent;
 | 
						|
 | 
						|
		size_t oldExtent = mBufferExtent;
 | 
						|
 | 
						|
		if (newOrigin != oldExtent)
 | 
						|
			mFile.seek (newOrigin);
 | 
						|
 | 
						|
		mBufferOrigin = mBufferExtent = newOrigin;
 | 
						|
 | 
						|
		size_t amountRequested = newExtent - newOrigin;
 | 
						|
 | 
						|
		size_t amountRead = mFile.read (mBuffer, amountRequested);
 | 
						|
 | 
						|
		if (amountRead != amountRequested)
 | 
						|
			throw std::runtime_error ("An unexpected condition occurred while reading from a file.");
 | 
						|
 | 
						|
		mBufferExtent = newExtent;
 | 
						|
	}
 | 
						|
 | 
						|
	LowLevelFile mFile;
 | 
						|
 | 
						|
	size_t mOrigin;
 | 
						|
	size_t mExtent;
 | 
						|
	size_t mPos;
 | 
						|
 | 
						|
	uint8_t mBuffer [sBufferSize];
 | 
						|
	size_t mBufferOrigin;
 | 
						|
	size_t mBufferExtent;
 | 
						|
};
 | 
						|
 | 
						|
} // end of unnamed namespace
 | 
						|
 | 
						|
Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length)
 | 
						|
{
 | 
						|
	return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length));
 | 
						|
}
 |