mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 09:26:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			123 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "constrainedfilestream.hpp"
 | 
						|
 | 
						|
#include <streambuf>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include "lowlevelfile.hpp"
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
// somewhat arbitrary though 64KB buffers didn't seem to improve performance any
 | 
						|
const size_t sBufferSize = 4096;
 | 
						|
}
 | 
						|
 | 
						|
namespace Files
 | 
						|
{
 | 
						|
    class ConstrainedFileStreamBuf : public std::streambuf
 | 
						|
    {
 | 
						|
 | 
						|
        size_t mOrigin;
 | 
						|
        size_t mSize;
 | 
						|
 | 
						|
        LowLevelFile mFile;
 | 
						|
 | 
						|
        char mBuffer[sBufferSize];
 | 
						|
 | 
						|
    public:
 | 
						|
        ConstrainedFileStreamBuf(const std::string &fname, size_t start, size_t length)
 | 
						|
        {
 | 
						|
            mFile.open (fname.c_str ());
 | 
						|
            mSize  = length != 0xFFFFFFFF ? length : mFile.size () - start;
 | 
						|
 | 
						|
            if (start != 0)
 | 
						|
                mFile.seek(start);
 | 
						|
 | 
						|
            setg(0,0,0);
 | 
						|
 | 
						|
            mOrigin = start;
 | 
						|
        }
 | 
						|
 | 
						|
        virtual int_type underflow()
 | 
						|
        {
 | 
						|
            if(gptr() == egptr())
 | 
						|
            {
 | 
						|
                size_t toRead = std::min((mOrigin+mSize)-(mFile.tell()), sBufferSize);
 | 
						|
                // Read in the next chunk of data, and set the read pointers on success
 | 
						|
                // Failure will throw exception in LowLevelFile
 | 
						|
                size_t got = mFile.read(mBuffer, toRead);
 | 
						|
                setg(&mBuffer[0], &mBuffer[0], &mBuffer[0]+got);
 | 
						|
            }
 | 
						|
            if(gptr() == egptr())
 | 
						|
                return traits_type::eof();
 | 
						|
 | 
						|
            return traits_type::to_int_type(*gptr());
 | 
						|
        }
 | 
						|
 | 
						|
        virtual pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode)
 | 
						|
        {
 | 
						|
            if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
 | 
						|
                return traits_type::eof();
 | 
						|
 | 
						|
            // new file position, relative to mOrigin
 | 
						|
            size_t newPos;
 | 
						|
            switch (whence)
 | 
						|
            {
 | 
						|
                case std::ios_base::beg:
 | 
						|
                    newPos = offset;
 | 
						|
                    break;
 | 
						|
                case std::ios_base::cur:
 | 
						|
                    newPos = (mFile.tell() - mOrigin - (egptr() - gptr())) + offset;
 | 
						|
                    break;
 | 
						|
                case std::ios_base::end:
 | 
						|
                    newPos = mSize + offset;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    return traits_type::eof();
 | 
						|
            }
 | 
						|
 | 
						|
            if (newPos > mSize)
 | 
						|
                return traits_type::eof();
 | 
						|
 | 
						|
            mFile.seek(mOrigin+newPos);
 | 
						|
 | 
						|
            // Clear read pointers so underflow() gets called on the next read attempt.
 | 
						|
            setg(0, 0, 0);
 | 
						|
 | 
						|
            return newPos;
 | 
						|
        }
 | 
						|
 | 
						|
        virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode)
 | 
						|
        {
 | 
						|
            if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
 | 
						|
                return traits_type::eof();
 | 
						|
 | 
						|
            if ((size_t)pos > mSize)
 | 
						|
                return traits_type::eof();
 | 
						|
 | 
						|
            mFile.seek(mOrigin + pos);
 | 
						|
 | 
						|
            // Clear read pointers so underflow() gets called on the next read attempt.
 | 
						|
            setg(0, 0, 0);
 | 
						|
            return pos;
 | 
						|
        }
 | 
						|
 | 
						|
    };
 | 
						|
 | 
						|
    ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length)
 | 
						|
        : std::istream(new ConstrainedFileStreamBuf(filename, start, length))
 | 
						|
    {
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    ConstrainedFileStream::~ConstrainedFileStream()
 | 
						|
    {
 | 
						|
        delete rdbuf();
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    IStreamPtr openConstrainedFileStream(const char *filename,
 | 
						|
                                                       size_t start, size_t length)
 | 
						|
    {
 | 
						|
        return IStreamPtr(new ConstrainedFileStream(filename, start, length));
 | 
						|
    }
 | 
						|
}
 |