git repos / blockattack-game

source/code/physfs_stream.hpp

browsing at commit = 30412ca6feb5352ee3f2eea89a4bc8686504252d

raw · blame · history

/*
physfs_stream.hpp
    Copyright (C) 2009 Poul Sander
        Copied from Warzone 2100
        http://svn.gna.org/viewcvs/warzone/branches/sound/lib/sound/general/physfs_stream.hpp?rev=3623
	Copyright (C) 2007 Warzone Resurrection Project
	Copyright (C) 2007 Giel van Schijndel <me@mortis.eu>
	Originally part of SuperTux:
	http://svn.berlios.de/svnroot/repos/supertux/trunk/supertux/src/physfs/physfs_stream.cpp revision 4176
	Copyright (C) 2006 Matthias Braun <matze@braunis.de>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Poul Sander
    blockattack@poulsander.com
*/

#ifndef __INCLUDED_PHYSFS_STREAM_HPP__
#define __INCLUDED_PHYSFS_STREAM_HPP__

#define BUF_SIZE 1024

#include <physfs.h>
#include <streambuf>
#include <istream>
#include <ostream>
/*Compared to Warzone 2100 boost:array has been replaced by vector to limit
 the compile requirements*/
#include <vector>

/** This an implementation of "File-based streams" as described in the
 *  ISO/IEC 14882:1998(E) C++ Standard in chapter 27 "Input/output library",
 *  section 8 "File-based streams". This implementation however uses PhysicsFS
 *  instead of the C-library's FILE functions. Also this implementation lacks
 *  an implementation of basic_fstream because PhysicsFS does not support
 *  opening a file both for reading as well as writing.
 *
 *  This header contains similar classes and typedefs as <fstream> does. Both
 *  similar in names as well as operation, the main difference is that these
 *  classes do _not_ reside in namespace std.
 */
namespace PhysFS
{
/** Typedefinitions mandated by the C++ standard in section 27.8.1
 */
template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
class physfs_filebuf;
typedef physfs_filebuf<char>        filebuf;
typedef physfs_filebuf<wchar_t>     wfilebuf;

template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
class physfs_ifstream;
typedef physfs_ifstream<char>       ifstream;
typedef physfs_ifstream<wchar_t>    wifstream;

template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
class physfs_ofstream;
typedef physfs_ofstream<char>       ofstream;
typedef physfs_ofstream<wchar_t>    wofstream;

/** This class implements a C++ streambuf object for physfs files.
 *  So that you can use normal istream and ostream operations on them.
 *  This class is similar to std::basic_filebuf as defined in
 *  section 27.8.1.1, except in name.
 */
template<typename _CharT, typename _Traits>
class physfs_filebuf : public std::basic_streambuf<_CharT, _Traits>
{
public:
	// Types:
	typedef _CharT                                  char_type;
	typedef _Traits                                 traits_type;
	typedef typename traits_type::int_type          int_type;
	typedef typename traits_type::pos_type          pos_type;
	typedef typename traits_type::off_type          off_type;

	typedef std::basic_streambuf<_CharT, _Traits>   __streambuf_type;
	typedef physfs_filebuf<_CharT, _Traits>         __filebuf_type;

	physfs_filebuf() :
		_file(0)
	{
		_buf.resize(BUF_SIZE);
	}

	virtual ~physfs_filebuf()
	{
		close();
	}

	bool is_open() const throw()
	{
		return (_file != 0);
	}

	__filebuf_type* open(const char* fileName, std::ios_base::openmode mode)
	{
		// Return failure if this file buf is open already
		if (is_open() != false)
			return 0;

		// Return failure if we are requested to open a file in an unsupported mode
		if (!(mode & std::ios_base::binary) ||
				(mode & std::ios_base::in &&
				 mode & std::ios_base::out))
			return 0;

		// Open the file
		if (mode & std::ios_base::out &&
				mode & std::ios_base::app)
		{
			_file = PHYSFS_openAppend(fileName);
			_writeStream = true;
		}
		else if (mode & std::ios_base::out)
		{
			_file = PHYSFS_openWrite(fileName);
			_writeStream = true;
		}
		else if (mode & std::ios_base::in)
		{
			_file = PHYSFS_openRead(fileName);
			_writeStream = false;
		}
		else
		{
			return 0;
		}

		if (!_file)
			return 0;

		if (mode & std::ios_base::ate &&
				mode & std::ios_base::in)
		{
			if (!PHYSFS_seek(_file, PHYSFS_fileLength(_file)))
			{
				close();
				return 0;
			}
		}

		return this;
	}

	__filebuf_type* close()
	{
		// Return failure if this file buf is closed already
		if (is_open() == false)
			return 0;

		sync();

		if (!PHYSFS_close(_file))
		{
			return 0;
		}

		_file = 0;

		return this;
	}

protected:
	// Read stuff:
	virtual int_type underflow()
	{
		if (!is_open() || _writeStream)
			return traits_type::eof();

		if(PHYSFS_eof(_file))
		{
			return traits_type::eof();
		}

		PHYSFS_sint64 objectsRead = PHYSFS_read(_file, &*_buf.begin(), sizeof(char_type), BUF_SIZE);

		if(objectsRead <= 0)
		{
			return traits_type::eof();
		}

		char_type* xend = (static_cast<size_t> (objectsRead) == BUF_SIZE) ? &*_buf.end() : &_buf[objectsRead];
		this->setg(&*_buf.begin(), &*_buf.begin(), xend);

		return traits_type::to_int_type(_buf.front());
	}

	virtual pos_type seekpos(pos_type pos, std::ios_base::openmode)
	{
		if (!is_open() || _writeStream)
			return pos_type(off_type(-1));

		if(PHYSFS_seek(_file, static_cast<PHYSFS_uint64> (pos)) == 0)
		{
			return pos_type(off_type(-1));
		}

		// the seek invalidated the buffer
		this->setg(&*_buf.begin(), &*_buf.begin(), &*_buf.begin());
		return pos;
	}

	virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
	{
		if (!is_open() || _writeStream)
			return pos_type(off_type(-1));

		off_type pos = off;

		switch (dir)
		{
		case std::ios_base::beg:
			break;

		case std::ios_base::cur:
		{
			PHYSFS_sint64 ptell = PHYSFS_tell(_file);
			pos_type buf_tell = static_cast<pos_type> (ptell) - static_cast<pos_type> (__streambuf_type::egptr() - __streambuf_type::gptr());
			if(off == 0)
			{
				return buf_tell;
			}

			pos += static_cast<off_type> (buf_tell);
			break;
		}

		case std::ios_base::end:
			pos = static_cast<off_type> (PHYSFS_fileLength(_file)) - pos;
			break;

		default:
			//assert(!"invalid seekdirection");
			return pos_type(off_type(-1));
		}

		return seekpos(static_cast<pos_type> (pos), mode);
	}

	// Write stuff:
	virtual int_type overflow(int_type c = traits_type::eof())
	{
		if (!is_open() || !_writeStream)
			return traits_type::eof();

		size_t size = __streambuf_type::pptr() - __streambuf_type::pbase();
		if(size == 0)
			return 0;

		PHYSFS_sint64 res = PHYSFS_write(_file, __streambuf_type::pbase(), sizeof(char_type), size);

		if(res <= 0)
			return traits_type::eof();

		if(!traits_type::eq_int_type(c, traits_type::eof()))
		{
			PHYSFS_sint64 res = PHYSFS_write(_file, &c, sizeof(char_type), 1);

			if(res <= 0)
				return traits_type::eof();
		}

		char_type* xend = (static_cast<size_t> (res) == BUF_SIZE) ? &*_buf.end() : &_buf[res];
		this->setp(&*_buf.begin(), xend);
		return 0;
	}

	virtual int sync()
	{
		if (!is_open() || !_writeStream)
			return -1;

		return overflow(traits_type::eof());
	}

	virtual std::streamsize showmanyc()
	{
		if (!is_open() || _writeStream)
			return -1;

		PHYSFS_sint64 fileSize = PHYSFS_fileLength(_file);

		return static_cast<int> (fileSize);
	}

private:
	PHYSFS_file* _file;
	bool _writeStream;
	std::vector<char_type> _buf;
};

/** This class implements a C++ istream object for physfs files.
 *  This class is similar to std::basic_ifstream as defined in
 *  section 27.8.1.5, except in name.
 */
template<typename _CharT, typename _Traits>
class physfs_ifstream : public std::basic_istream<_CharT, _Traits>
{
public:
	// Types:
	typedef _CharT                                  char_type;
	typedef _Traits                                 traits_type;
	typedef typename traits_type::int_type          int_type;
	typedef typename traits_type::pos_type          pos_type;
	typedef typename traits_type::off_type          off_type;

	typedef physfs_filebuf<_CharT, _Traits>         __filebuf_type;
	typedef std::basic_istream<_CharT, _Traits>     __istream_type;

	physfs_ifstream() throw() :
		__istream_type(&_sb)
	{
	}

	explicit physfs_ifstream(const char* fileName, std::ios_base::openmode mode = std::ios_base::binary) :
		__istream_type(&_sb)
	{
		open(fileName, mode);
	}

	const __filebuf_type* rdbuf() const throw()
	{
		return &_sb;
	}

	__filebuf_type* rdbuf() throw()
	{
		return &_sb;
	}

	bool is_open() const throw()
	{
		return rdbuf()->is_open();
	}

	void open(const char* fileName, std::ios_base::openmode mode = std::ios_base::binary)
	{
		if (!rdbuf()->open(fileName, mode | std::ios_base::in))
		{
			__istream_type::setstate(std::ios_base::failbit);
		}
	}

	void close()
	{
		if (!rdbuf()->close())
		{
			__istream_type::setstate(std::ios_base::failbit);
		}
	}

private:
	__filebuf_type _sb;
};

/** This class implements a C++ ostream object for physfs files.
 *  This class is similar to std::basic_ofstream as defined in
 *  section 27.8.1.8, except in name.
 */
template<typename _CharT, typename _Traits>
class physfs_ofstream : public std::basic_ostream<_CharT, _Traits>
{
public:
	// Types:
	typedef _CharT                                  char_type;
	typedef _Traits                                 traits_type;
	typedef typename traits_type::int_type          int_type;
	typedef typename traits_type::pos_type          pos_type;
	typedef typename traits_type::off_type          off_type;

	typedef physfs_filebuf<_CharT, _Traits>         __filebuf_type;
	typedef std::basic_ostream<_CharT, _Traits>     __ostream_type;

	physfs_ofstream() throw() :
		__ostream_type(&_sb)
	{
	}

	explicit physfs_ofstream(const char* fileName, std::ios_base::openmode mode = std::ios_base::binary) :
		__ostream_type(&_sb)
	{
		open(fileName, mode);
	}

	const __filebuf_type* rdbuf() const throw()
	{
		return &_sb;
	}

	__filebuf_type* rdbuf() throw()
	{
		return &_sb;
	}

	bool is_open() const throw()
	{
		return rdbuf()->is_open();
	}

	void open(const char* fileName, std::ios_base::openmode mode = std::ios_base::binary)
	{
		if (!rdbuf()->open(fileName, mode | std::ios_base::out))
		{
			__ostream_type::setstate(std::ios_base::failbit);
		}
	}

	void close()
	{
		if (!rdbuf()->close())
		{
			__ostream_type::setstate(std::ios_base::failbit);
		}
	}

private:
	__filebuf_type _sb;
};
}

#endif // __INCLUDED_PHYSFS_STREAM_HPP__