Merge remote branch 'zini/master' into nif-cleanup

Conflicts:
	components/bsa/bsa_archive.cpp
actorid
Chris Robinson 12 years ago
commit b4c8375f3c

@ -101,9 +101,8 @@ ENDIF()
set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(MANGLE_VFS ${LIBDIR}/mangle/vfs/servers/ogre_vfs.cpp)
set(MANGLE_INPUT ${LIBDIR}/mangle/input/servers/ois_driver.cpp)
set(MANGLE_ALL ${MANGLE_VFS} ${MANGLE_INPUT})
set(MANGLE_ALL ${MANGLE_INPUT})
source_group(libs\\mangle FILES ${MANGLE_ALL})
set(OENGINE_OGRE

@ -79,7 +79,6 @@ ENDIF(WIN32)
ENDIF(OGRE_STATIC)
add_executable(openmw
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
${COMPONENT_FILES}
${OPENMW_FILES}
${GAME} ${GAME_HEADER}
${APPLE_BUNDLE_RESOURCES}

@ -28,13 +28,11 @@
#include <OgreArchiveFactory.h>
#include <OgreArchiveManager.h>
#include "bsa_file.hpp"
#include <libs/mangle/stream/clients/ogre_datastream.hpp>
namespace
{
using namespace Ogre;
using namespace Mangle::Stream;
using namespace Bsa;
struct ciLessBoost : std::binary_function<std::string, std::string, bool>
@ -213,10 +211,7 @@ public:
BSAFile *narc = const_cast<BSAFile*>(&arc);
// Open the file
StreamPtr strm = narc->getFile(filename.c_str());
// Wrap it into an Ogre::DataStream.
return DataStreamPtr(new Mangle2OgreStream(strm));
return narc->getFile(filename.c_str());
}
bool exists(const String& filename) {

@ -23,171 +23,235 @@
#include "bsa_file.hpp"
#include <libs/mangle/stream/servers/file_stream.hpp>
#include <libs/mangle/stream/filters/slice_stream.hpp>
#include <stdexcept>
#include <stdlib.h>
#include <assert.h>
#include <OgreDataStream.h>
using namespace std;
using namespace Mangle::Stream;
using namespace Bsa;
class ConstrainedDataStream : public Ogre::DataStream {
std::ifstream mStream;
const size_t mStart;
size_t mPos;
bool mIsEOF;
public:
ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length)
: mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false)
{
mSize = length;
if(!mStream.seekg(mStart, std::ios_base::beg))
throw std::runtime_error("Error seeking to start of BSA entry");
}
ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname,
size_t start, size_t length)
: Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary),
mStart(start), mPos(0), mIsEOF(false)
{
mSize = length;
if(!mStream.seekg(mStart, std::ios_base::beg))
throw std::runtime_error("Error seeking to start of BSA entry");
}
virtual size_t read(void *buf, size_t count)
{
mStream.clear();
if(count > mSize-mPos)
{
count = mSize-mPos;
mIsEOF = true;
}
mStream.read(reinterpret_cast<char*>(buf), count);
count = mStream.gcount();
mPos += count;
return count;
}
virtual void skip(long count)
{
if((count >= 0 && (size_t)count <= mSize-mPos) ||
(count < 0 && (size_t)-count <= mPos))
{
mStream.clear();
if(mStream.seekg(count, std::ios_base::cur))
{
mPos += count;
mIsEOF = false;
}
}
}
virtual void seek(size_t pos)
{
if(pos < mSize)
{
mStream.clear();
if(mStream.seekg(pos+mStart, std::ios_base::beg))
{
mPos = pos;
mIsEOF = false;
}
}
}
virtual size_t tell() const
{ return mPos; }
virtual bool eof() const
{ return mIsEOF; }
virtual void close()
{ mStream.close(); }
};
/// Error handling
void BSAFile::fail(const string &msg)
{
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename);
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename);
}
/// Read header information from the input source
void BSAFile::readHeader()
{
/*
* The layout of a BSA archive is as follows:
*
* - 12 bytes header, contains 3 ints:
* id number - equal to 0x100
* dirsize - size of the directory block (see below)
* numfiles - number of files
*
* ---------- start of directory block -----------
*
* - 8 bytes*numfiles, each record contains:
* fileSize
* offset into data buffer (see below)
*
* - 4 bytes*numfiles, each record is an offset into the following name buffer
*
* - name buffer, indexed by the previous table, each string is
* null-terminated. Size is (dirsize - 12*numfiles).
*
* ---------- end of directory block -------------
*
* - 8*filenum - hash table block, we currently ignore this
*
* ----------- start of data buffer --------------
*
* - The rest of the archive is file data, indexed by the
* offsets in the directory block. The offsets start at 0 at
* the beginning of this buffer.
*
*/
assert(!isLoaded);
assert(input);
assert(input->hasSize);
assert(input->hasPosition);
assert(input->isSeekable);
// Total archive size
size_t fsize = input->size();
if( fsize < 12 )
fail("File too small to be a valid BSA archive");
// Get essential header numbers
size_t dirsize, filenum;
{
// First 12 bytes
uint32_t head[3];
input->read(head, 12);
if(head[0] != 0x100)
fail("Unrecognized BSA header");
// Total number of bytes used in size/offset-table + filename
// sections.
dirsize = head[1];
// Number of files
filenum = head[2];
}
// Each file must take up at least 21 bytes of data in the bsa. So
// if files*21 overflows the file size then we are guaranteed that
// the archive is corrupt.
if( (filenum*21 > fsize -12) ||
(dirsize+8*filenum > fsize -12) )
fail("Directory information larger than entire archive");
// Read the offset info into a temporary buffer
vector<uint32_t> offsets(3*filenum);
input->read(&offsets[0], 12*filenum);
// Read the string table
stringBuf.resize(dirsize-12*filenum);
input->read(&stringBuf[0], stringBuf.size());
// Check our position
assert(input->tell() == 12+dirsize);
// Calculate the offset of the data buffer. All file offsets are
// relative to this. 12 header bytes + directory + hash table
// (skipped)
size_t fileDataOffset = 12 + dirsize + 8*filenum;
// Set up the the FileStruct table
files.resize(filenum);
for(size_t i=0;i<filenum;i++)
/*
* The layout of a BSA archive is as follows:
*
* - 12 bytes header, contains 3 ints:
* id number - equal to 0x100
* dirsize - size of the directory block (see below)
* numfiles - number of files
*
* ---------- start of directory block -----------
*
* - 8 bytes*numfiles, each record contains:
* fileSize
* offset into data buffer (see below)
*
* - 4 bytes*numfiles, each record is an offset into the following name buffer
*
* - name buffer, indexed by the previous table, each string is
* null-terminated. Size is (dirsize - 12*numfiles).
*
* ---------- end of directory block -------------
*
* - 8*filenum - hash table block, we currently ignore this
*
* ----------- start of data buffer --------------
*
* - The rest of the archive is file data, indexed by the
* offsets in the directory block. The offsets start at 0 at
* the beginning of this buffer.
*
*/
assert(!isLoaded);
std::ifstream input(filename.c_str(), std::ios_base::binary);
// Total archive size
size_t fsize = 0;
if(input.seekg(0, std::ios_base::end))
{
FileStruct &fs = files[i];
fs.fileSize = offsets[i*2];
fs.offset = offsets[i*2+1] + fileDataOffset;
fs.name = &stringBuf[offsets[2*filenum+i]];
fsize = input.tellg();
input.seekg(0);
}
if(fsize < 12)
fail("File too small to be a valid BSA archive");
// Get essential header numbers
size_t dirsize, filenum;
{
// First 12 bytes
uint32_t head[3];
input.read(reinterpret_cast<char*>(head), 12);
if(head[0] != 0x100)
fail("Unrecognized BSA header");
// Total number of bytes used in size/offset-table + filename
// sections.
dirsize = head[1];
// Number of files
filenum = head[2];
}
// Each file must take up at least 21 bytes of data in the bsa. So
// if files*21 overflows the file size then we are guaranteed that
// the archive is corrupt.
if((filenum*21 > fsize -12) || (dirsize+8*filenum > fsize -12) )
fail("Directory information larger than entire archive");
// Read the offset info into a temporary buffer
vector<uint32_t> offsets(3*filenum);
input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
if(fs.offset + fs.fileSize > fsize)
fail("Archive contains offsets outside itself");
// Read the string table
stringBuf.resize(dirsize-12*filenum);
input.read(&stringBuf[0], stringBuf.size());
// Add the file name to the lookup
lookup[fs.name] = i;
// Check our position
assert(input.tellg() == static_cast<int> (12+dirsize));
// Calculate the offset of the data buffer. All file offsets are
// relative to this. 12 header bytes + directory + hash table
// (skipped)
size_t fileDataOffset = 12 + dirsize + 8*filenum;
// Set up the the FileStruct table
files.resize(filenum);
for(size_t i=0;i<filenum;i++)
{
FileStruct &fs = files[i];
fs.fileSize = offsets[i*2];
fs.offset = offsets[i*2+1] + fileDataOffset;
fs.name = &stringBuf[offsets[2*filenum+i]];
if(fs.offset + fs.fileSize > fsize)
fail("Archive contains offsets outside itself");
// Add the file name to the lookup
lookup[fs.name] = i;
}
isLoaded = true;
isLoaded = true;
}
/// Get the index of a given file name, or -1 if not found
int BSAFile::getIndex(const char *str) const
{
Lookup::const_iterator it;
it = lookup.find(str);
Lookup::const_iterator it = lookup.find(str);
if(it == lookup.end())
return -1;
if(it == lookup.end()) return -1;
else
{
int res = it->second;
assert(res >= 0 && res < static_cast<int> (files.size()));
return res;
}
int res = it->second;
assert(res >= 0 && (size_t)res < files.size());
return res;
}
/// Open an archive file.
void BSAFile::open(const string &file)
{
filename = file;
input = StreamPtr(new FileStream(file));
readHeader();
filename = file;
readHeader();
}
/** Open an archive from a generic stream. The 'name' parameter is
used for error messages.
*/
void BSAFile::open(StreamPtr inp, const string &name)
Ogre::DataStreamPtr BSAFile::getFile(const char *file)
{
filename = name;
input = inp;
readHeader();
}
StreamPtr BSAFile::getFile(const char *file)
{
assert(file);
int i = getIndex(file);
if(i == -1)
fail("File not found: " + string(file));
FileStruct &fs = files[i];
assert(file);
int i = getIndex(file);
if(i == -1)
fail("File not found: " + string(file));
return StreamPtr(new SliceStream(input, fs.offset, fs.fileSize));
const FileStruct &fs = files[i];
return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize));
}

@ -24,13 +24,15 @@
#ifndef BSA_BSA_FILE_H
#define BSA_BSA_FILE_H
#include <libs/mangle/stream/stream.hpp>
#include <libs/platform/stdint.h>
#include <libs/platform/strings.h>
#include <string>
#include <vector>
#include <map>
#include <OgreDataStream.h>
namespace Bsa
{
@ -39,98 +41,85 @@ namespace Bsa
*/
class BSAFile
{
public:
/// Represents one file entry in the archive
struct FileStruct
{
// File size and offset in file. We store the offset from the
// beginning of the file, not the offset into the data buffer
// (which is what is stored in the archive.)
uint32_t fileSize, offset;
// Zero-terminated file name
char* name;
};
typedef std::vector<FileStruct> FileList;
private:
/// The archive source
Mangle::Stream::StreamPtr input;
/// Table of files in this archive
FileList files;
/// Filename string buffer
std::vector<char> stringBuf;
/// True when an archive has been loaded
bool isLoaded;
/// Used for error messages
std::string filename;
/// Case insensitive string comparison
struct iltstr
{
bool operator()(const char *s1, const char *s2) const
{ return strcasecmp(s1,s2) < 0; }
};
/** A map used for fast file name lookup. The value is the index into
the files[] vector above. The iltstr ensures that file name
checks are case insensitive.
*/
typedef std::map<const char*, int, iltstr> Lookup;
Lookup lookup;
/// Error handling
void fail(const std::string &msg);
/// Read header information from the input source
void readHeader();
/// Get the index of a given file name, or -1 if not found
int getIndex(const char *str) const;
public:
/* -----------------------------------
* BSA management methods
* -----------------------------------
*/
BSAFile()
: input(), isLoaded(false) {}
/// Open an archive file.
void open(const std::string &file);
/** Open an archive from a generic stream. The 'name' parameter is
used for error messages.
*/
void open(Mangle::Stream::StreamPtr inp, const std::string &name);
/* -----------------------------------
* Archive file routines
* -----------------------------------
*/
/// Check if a file exists
bool exists(const char *file) const { return getIndex(file) != -1; }
/** Open a file contained in the archive. Throws an exception if the
file doesn't exist.
NOTE: All files opened from one archive will share a common file
handle. This is NOT thread safe.
*/
Mangle::Stream::StreamPtr getFile(const char *file);
/// Get a list of all files
const FileList &getList() const
public:
/// Represents one file entry in the archive
struct FileStruct
{
// File size and offset in file. We store the offset from the
// beginning of the file, not the offset into the data buffer
// (which is what is stored in the archive.)
uint32_t fileSize, offset;
// Zero-terminated file name
const char *name;
};
typedef std::vector<FileStruct> FileList;
private:
/// Table of files in this archive
FileList files;
/// Filename string buffer
std::vector<char> stringBuf;
/// True when an archive has been loaded
bool isLoaded;
/// Used for error messages
std::string filename;
/// Case insensitive string comparison
struct iltstr
{
bool operator()(const char *s1, const char *s2) const
{ return strcasecmp(s1,s2) < 0; }
};
/** A map used for fast file name lookup. The value is the index into
the files[] vector above. The iltstr ensures that file name
checks are case insensitive.
*/
typedef std::map<const char*, int, iltstr> Lookup;
Lookup lookup;
/// Error handling
void fail(const std::string &msg);
/// Read header information from the input source
void readHeader();
/// Get the index of a given file name, or -1 if not found
int getIndex(const char *str) const;
public:
/* -----------------------------------
* BSA management methods
* -----------------------------------
*/
BSAFile()
: isLoaded(false)
{ }
/// Open an archive file.
void open(const std::string &file);
/* -----------------------------------
* Archive file routines
* -----------------------------------
*/
/// Check if a file exists
bool exists(const char *file) const
{ return getIndex(file) != -1; }
/** Open a file contained in the archive. Throws an exception if the
file doesn't exist.
*/
Ogre::DataStreamPtr getFile(const char *file);
/// Get a list of all files
const FileList &getList() const
{ return files; }
};

@ -27,7 +27,7 @@ void ESMReader::restoreContext(const ESM_Context &rc)
void ESMReader::close()
{
mEsm.reset();
mEsm.setNull();
mCtx.filename.clear();
mCtx.leftFile = 0;
mCtx.leftRec = 0;
@ -37,7 +37,7 @@ void ESMReader::close()
mCtx.subName.val = 0;
}
void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name)
{
close();
mEsm = _esm;
@ -57,7 +57,7 @@ void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
mSpf = SF_Other;
}
void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name)
{
openRaw(_esm, name);
@ -107,14 +107,16 @@ void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
void ESMReader::open(const std::string &file)
{
using namespace Mangle::Stream;
open(StreamPtr(new FileStream(file)), file);
std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary);
// Ogre will delete the stream for us
open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file);
}
void ESMReader::openRaw(const std::string &file)
{
using namespace Mangle::Stream;
openRaw(StreamPtr(new FileStream(file)), file);
std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary);
// Ogre will delete the stream for us
openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file);
}
int64_t ESMReader::getHNLong(const char *name)
@ -339,7 +341,7 @@ void ESMReader::fail(const std::string &msg)
ss << "\n File: " << mCtx.filename;
ss << "\n Record: " << mCtx.recName.toString();
ss << "\n Subrecord: " << mCtx.subName.toString();
if (mEsm != NULL)
if (!mEsm.isNull())
ss << "\n Offset: 0x" << hex << mEsm->tell();
throw std::runtime_error(ss.str());
}

@ -11,8 +11,8 @@
#include <sstream>
#include <stdexcept>
#include <libs/mangle/stream/stream.hpp>
#include <libs/mangle/stream/servers/file_stream.hpp>
#include <OgreDataStream.h>
#include <components/misc/stringops.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -183,11 +183,11 @@ public:
/// Raw opening. Opens the file and sets everything up but doesn't
/// parse the header.
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name);
void openRaw(Ogre::DataStreamPtr _esm, const std::string &name);
/// Load ES file from a new stream, parses the header. Closes the
/// currently open file first, if any.
void open(Mangle::Stream::StreamPtr _esm, const std::string &name);
void open(Ogre::DataStreamPtr _esm, const std::string &name);
void open(const std::string &file);
@ -354,7 +354,7 @@ public:
void setEncoding(const std::string& encoding);
private:
Mangle::Stream::StreamPtr mEsm;
Ogre::DataStreamPtr mEsm;
ESM_Context mCtx;

@ -32,7 +32,6 @@
#include <string>
#include <boost/algorithm/string.hpp>
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
#include "../nif/nif_file.hpp"
#include "../nif/node.hpp"
#include "../nif/data.hpp"

@ -8,41 +8,7 @@
using namespace Terrain;
static class ESMLandStream : public Mangle::Stream
{
public:
ESMLandStream(ESM::Land *l, ESM::ESMReader &r)
{
}
};
bool ESMLandFactory::has(int x, int y)
{
return store.landscapes.has(x,y);
}
LandDataPtr get(int x, int y, LandInfo &info)
{
assert(has(x,y));
// Set up the info
info.grid = LGT_Quadratic;
info.data = LDT_Float;
const float SIZE = 8192; // CHECK
info.xsize = SIZE;
info.ysize = SIZE;
info.numx = 65;
info.numy = 65;
info.xoffset = SIZE*x;
info.yoffset = SIZE*y;
// Get the Land struct from store
ESM::Land* land = store.landscapes.find(x,y);
assert(land->hasData);
// Create a stream for the data and return it.
LandDataPtr ptr(new ESMLandStream(land, reader));
return ptr;
}

@ -32,10 +32,6 @@ namespace Terrain
// True if this factory has any data for the given grid cell.
bool has(int x, int y);
// Return stream to data for this cell. Additional data about the
// landscape is returned through the LandInfo struct.
LandDataPtr get(int x, int y, LandInfo &info);
};
}
#endif

@ -1,8 +1,6 @@
#ifndef TERRAIN_LAND_FACTORY_H
#define TERRAIN_LAND_FACTORY_H
#include <mangle/stream/stream.hpp>
namespace Terrain
{
enum LandInfoGridType
@ -32,12 +30,6 @@ namespace Terrain
float xoffset, yoffset;
};
/* We use normal streams for data. This allows us to just pass (for
example) a file stream as height map input later, with no extra
fuzz.
*/
typedef Mangle::Stream::StreamPtr LandDataPtr;
/*
Factory class that provides streams to land data cells. Each
"cell" has a unique integer coordinate in the plane.
@ -46,10 +38,6 @@ namespace Terrain
{
// True if this factory has any data for the given grid cell.
virtual bool has(int x, int y) = 0;
// Return stream to data for this cell. Additional data about the
// landscape is returned through the LandInfo struct.
virtual LandDataPtr get(int x, int y, LandInfo &info) = 0;
};
}
#endif

@ -1,32 +0,0 @@
#include "audiere_file.hpp"
using namespace audiere;
using namespace Mangle::Stream;
bool AudiereFile::seek(int pos, SeekMode mode)
{
assert(inp->isSeekable);
assert(inp->hasPosition);
size_t newPos;
switch(mode)
{
case BEGIN: newPos = pos; break;
case CURRENT: newPos = pos+tell(); break;
case END:
// Seeking from the end. This requires that we're able to get
// the entire size of the stream. The pos also has to be
// non-positive.
assert(inp->hasSize);
assert(pos <= 0);
newPos = inp->size() + pos;
break;
default:
assert(0 && "invalid seek mode");
}
inp->seek(newPos);
return inp->tell() == newPos;
}

@ -1,38 +0,0 @@
#ifndef MANGLE_STREAM_AUDIERECLIENT_H
#define MANGLE_STREAM_AUDIERECLIENT_H
#include <audiere.h>
#include <assert.h>
#include "../stream.hpp"
namespace Mangle {
namespace Stream {
/** @brief An Audiere::File that wraps a Mangle::Stream input.
This lets Audiere read sound files from any generic archive or
file manager that supports Mangle streams.
*/
class AudiereFile : public audiere::RefImplementation<audiere::File>
{
StreamPtr inp;
public:
AudiereFile(StreamPtr _inp)
: inp(_inp) {}
/// Read 'count' bytes, return bytes successfully read
int ADR_CALL read(void *buf, int count)
{ return inp->read(buf,count); }
/// Seek, relative to specified seek mode. Returns true if successful.
bool ADR_CALL seek(int pos, audiere::File::SeekMode mode);
/// Get current position
int ADR_CALL tell()
{ assert(inp->hasPosition); return inp->tell(); }
};
}} // namespaces
#endif

@ -1,221 +0,0 @@
#include "io_stream.hpp"
// This seems to work
#ifndef EOF
#define EOF -1
#endif
using namespace Mangle::Stream;
#define BSIZE 1024
// Streambuf for normal stream reading
class _istreambuf : public std::streambuf
{
StreamPtr client;
char buf[BSIZE];
public:
_istreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isReadable);
assert(!client->hasPtr);
// Tell streambuf to delegate reading operations to underflow()
setg(NULL,NULL,NULL);
// Disallow writing
setp(NULL,NULL);
}
/* Underflow is called when there is no more info to read in the
input buffer. We need to refill buf with new data (if any), and
set up the internal pointers with setg() to reflect the new
state.
*/
int underflow()
{
// Read some more data
size_t read = client->read(buf, BSIZE);
assert(read <= BSIZE);
// If we're out of data, then EOF
if(read == 0)
return EOF;
// Otherwise, set up input buffer
setg(buf, buf, buf+read);
// Return the first char
return *((unsigned char*)buf);
}
// Seek stream, if the source supports it. Ignores the second
// parameter as Mangle doesn't separate input and output pointers.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
{
// Does this stream know how to seek?
if(!client->isSeekable || !client->hasPosition)
// If not, signal an error.
return -1;
// Set stream position and reset the buffer.
client->seek(pos);
setg(NULL,NULL,NULL);
return client->tell();
}
};
// Streambuf optimized for pointer-based input streams
class _ptrstreambuf : public std::streambuf
{
StreamPtr client;
public:
_ptrstreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isReadable);
assert(client->hasPtr);
// seekpos() does all the work
seekpos(0);
}
// Underflow is only called when we're at the end of the file
int underflow() { return EOF; }
// Seek to a new position within the memory stream. This bypasses
// client->seek() entirely so isSeekable doesn't have to be set.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
{
// All pointer streams need a size
assert(client->hasSize);
// Figure out how much will be left of the stream after seeking
size_t size = client->size() - pos;
// Get a pointer
char* ptr = (char*)client->getPtr(pos,size);
// And use it
setg(ptr,ptr,ptr+size);
return pos;
}
};
// Streambuf for stream writing
class _ostreambuf : public std::streambuf
{
StreamPtr client;
char buf[BSIZE];
public:
_ostreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isWritable);
// Inform streambuf about our nice buffer
setp(buf, buf+BSIZE);
// Disallow reading
setg(NULL,NULL,NULL);
}
/* Sync means to flush (write) all current data to the output
stream. It will also set up the entire output buffer to be usable
again.
*/
int sync()
{
// Get the number of bytes that streambuf wants us to write
int num = pptr() - pbase();
assert(num >= 0);
// Is there any work to do?
if(num == 0) return 0;
if((int)client->write(pbase(), num) != num)
// Inform caller that writing failed
return -1;
// Reset output buffer pointers
setp(buf, buf+BSIZE);
// No error
return 0;
}
/* Called whenever the output buffer is full.
*/
int overflow(int c)
{
// First, write all existing data
if(sync()) return EOF;
// Put the requested character in the next round of output
if(c != EOF)
{
*pptr() = c;
pbump(1);
}
// No error
return 0;
}
// Seek stream, if the source supports it.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out)
{
if(!client->isSeekable || !client->hasPosition)
return -1;
// Flush data and reset buffers
sync();
// Set stream position
client->seek(pos);
return client->tell();
}
};
MangleIStream::MangleIStream(StreamPtr inp)
: std::istream(NULL)
{
assert(inp->isReadable);
// Pick the right streambuf implementation based on whether the
// input supports pointers or not.
if(inp->hasPtr)
buf = new _ptrstreambuf(inp);
else
buf = new _istreambuf(inp);
rdbuf(buf);
}
MangleIStream::~MangleIStream()
{
delete buf;
}
MangleOStream::MangleOStream(StreamPtr out)
: std::ostream(NULL)
{
assert(out->isWritable);
buf = new _ostreambuf(out);
rdbuf(buf);
}
MangleOStream::~MangleOStream()
{
// Make sure we don't have lingering data on exit
flush();
delete buf;
}

@ -1,43 +0,0 @@
#ifndef MANGLE_STREAM_IOSTREAM_H
#define MANGLE_STREAM_IOSTREAM_H
#include <assert.h>
#include "../stream.hpp"
#include <iostream>
namespace Mangle {
namespace Stream {
/** This file contains classes for wrapping an std::istream or
std::ostream around a Mangle::Stream.
This allows you to use Mangle streams in places that require std
streams.
This is much easier than trying to make your own custom streams
into iostreams. The std::iostream interface is horrible and NOT
designed for easy subclassing. Create a Mangle::Stream instead,
and use this wrapper.
*/
// An istream wrapping a readable Mangle::Stream. Has extra
// optimizations for pointer-based streams.
class MangleIStream : public std::istream
{
std::streambuf *buf;
public:
MangleIStream(StreamPtr inp);
~MangleIStream();
};
// An ostream wrapping a writable Mangle::Stream.
class MangleOStream : public std::ostream
{
std::streambuf *buf;
public:
MangleOStream(StreamPtr inp);
~MangleOStream();
};
}} // namespaces
#endif

@ -1,68 +0,0 @@
#ifndef MANGLE_STREAM_OGRECLIENT_H
#define MANGLE_STREAM_OGRECLIENT_H
#include <OgreDataStream.h>
#include <assert.h>
#include "../stream.hpp"
namespace Mangle {
namespace Stream {
/** An OGRE DataStream that wraps a Mangle::Stream input.
This has been built and tested against OGRE 1.6.2. You might have
to make your own modifications if you're working with newer (or
older) versions.
*/
class Mangle2OgreStream : public Ogre::DataStream
{
StreamPtr inp;
void init()
{
// Get the size, if possible
if(inp->hasSize)
mSize = inp->size();
// Allow writing if inp supports it
if(inp->isWritable)
mAccess |= Ogre::DataStream::WRITE;
}
public:
/// Constructor without name
Mangle2OgreStream(StreamPtr _inp)
: inp(_inp) { init(); }
/// Constructor for a named data stream
Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp)
: Ogre::DataStream(name), inp(_inp) { init(); }
// Only implement the DataStream functions we have to implement
size_t read(void *buf, size_t count)
{ return inp->read(buf,count); }
size_t write(const void *buf, size_t count)
{ assert(inp->isWritable); return inp->write(buf,count); }
void skip(long count)
{
assert(inp->isSeekable && inp->hasPosition);
inp->seek(inp->tell() + count);
}
void seek(size_t pos)
{ assert(inp->isSeekable); inp->seek(pos); }
size_t tell() const
{ assert(inp->hasPosition); return inp->tell(); }
bool eof() const { return inp->eof(); }
/// Does nothing
void close() {}
};
}} // namespaces
#endif

@ -1,74 +0,0 @@
#ifndef MANGLE_STREAM_BUFFER_H
#define MANGLE_STREAM_BUFFER_H
#include "../servers/memory_stream.hpp"
#include <vector>
namespace Mangle {
namespace Stream {
/** A Stream that reads another Stream into a buffer, and serves it as
a MemoryStream. Might be expanded with other capabilities later.
*/
class BufferStream : public MemoryStream
{
std::vector<char> buffer;
public:
/*
input = stream to copy
ADD = each read increment (for streams without size())
*/
BufferStream(StreamPtr input, size_t ADD = 32*1024)
{
assert(input);
// Allocate memory, read the stream into it. Then call set()
if(input->hasSize)
{
// We assume that we can get the position as well
assert(input->hasPosition);
// Calculate how much is left of the stream
size_t left = input->size() - input->tell();
// Allocate the buffer and fill it
buffer.resize(left);
input->read(&buffer[0], left);
}
else
{
// We DON'T know how big the stream is. We'll have to read
// it in increments.
size_t len=0, newlen;
while(!input->eof())
{
// Read one block
newlen = len + ADD;
buffer.resize(newlen);
size_t read = input->read(&buffer[len], ADD);
// Increase the total length
len += read;
// If we read less than expected, we should be at the
// end of the stream
assert(read == ADD || (read < ADD && input->eof()));
}
// Downsize to match the real length
buffer.resize(len);
}
// After the buffer has been filled, set up our MemoryStream
// ancestor to reference it.
set(&buffer[0], buffer.size());
}
};
typedef boost::shared_ptr<BufferStream> BufferStreamPtr;
}} // namespaces
#endif

@ -1,46 +0,0 @@
#ifndef MANGLE_STREAM_FILTER_H
#define MANGLE_STREAM_FILTER_H
#include "../stream.hpp"
namespace Mangle {
namespace Stream {
/** A stream that filters another stream with no changes. Intended as
a base class for other filters.
*/
class PureFilter : public Stream
{
protected:
StreamPtr src;
public:
PureFilter() {}
PureFilter(StreamPtr _src)
{ setStream(_src); }
void setStream(StreamPtr _src)
{
src = _src;
isSeekable = src->isSeekable;
isWritable = src->isWritable;
hasPosition = src->hasPosition;
hasSize = src->hasSize;
hasPtr = src->hasPtr;
}
size_t read(void *buf, size_t count) { return src->read(buf, count); }
size_t write(const void *buf, size_t count) { return src->write(buf,count); }
void flush() { src->flush(); }
void seek(size_t pos) { src->seek(pos); }
size_t tell() const { return src->tell(); }
size_t size() const { return src->size(); }
bool eof() const { return src->eof(); }
const void *getPtr() { return src->getPtr(); }
const void *getPtr(size_t size) { return src->getPtr(size); }
const void *getPtr(size_t pos, size_t size)
{ return src->getPtr(pos, size); }
};
}} // namespaces
#endif

@ -1,101 +0,0 @@
#ifndef MANGLE_STREAM_SLICE_H
#define MANGLE_STREAM_SLICE_H
#include "../stream.hpp"
namespace Mangle {
namespace Stream {
/** A Stream that represents a subset (called a slice) of another stream.
*/
class SliceStream : public Stream
{
StreamPtr src;
size_t offset, length, pos;
public:
SliceStream(StreamPtr _src, size_t _offset, size_t _length)
: src(_src), offset(_offset), length(_length), pos(0)
{
assert(src->hasSize);
assert(src->isSeekable);
// Make sure we can actually fit inside the source stream
assert(src->size() >= offset+length);
isSeekable = true;
hasPosition = true;
hasSize = true;
hasPtr = src->hasPtr;
isWritable = src->isWritable;
}
size_t read(void *buf, size_t count)
{
// Check that we're not reading past our slice
if(count > length-pos)
count = length-pos;
// Seek into place and start reading
src->seek(offset+pos);
count = src->read(buf, count);
pos += count;
assert(pos <= length);
return count;
}
// Note that writing to a slice does not allow you to append data,
// you may only overwrite existing data.
size_t write(const void *buf, size_t count)
{
assert(isWritable);
// Check that we're not reading past our slice
if(count > length-pos)
count = length-pos;
// Seek into place and action
src->seek(offset+pos);
count = src->write(buf, count);
pos += count;
assert(pos <= length);
return count;
}
void seek(size_t _pos)
{
pos = _pos;
if(pos > length) pos = length;
}
bool eof() const { return pos == length; }
size_t tell() const { return pos; }
size_t size() const { return length; }
void flush() { src->flush(); }
const void *getPtr() { return getPtr(0, length); }
const void *getPtr(size_t size)
{
const void *ptr = getPtr(pos, size);
seek(pos+size);
return ptr;
}
const void *getPtr(size_t pos, size_t size)
{
// Boundry checks on pos and size. Bounding the size is
// important even when getting pointers, as the source stream
// may use the size parameter for something (such as memory
// mapping or buffering.)
if(pos > length) pos = length;
if(pos+size > length) size = length-pos;
// Ask the source to kindly give us a pointer
return src->getPtr(offset+pos, size);
}
};
typedef boost::shared_ptr<SliceStream> SliceStreamPtr;
}} // namespaces
#endif

@ -1,32 +0,0 @@
#ifndef MANGLE_STREAM_FILESERVER_H
#define MANGLE_STREAM_FILESERVER_H
#include "std_stream.hpp"
#include <fstream>
#include <stdexcept>
namespace Mangle {
namespace Stream {
/** Very simple file input stream, based on std::ifstream
*/
class FileStream : public StdStream
{
std::ifstream file;
public:
FileStream(const std::string &name)
: StdStream(&file)
{
file.open(name.c_str(), std::ios::binary);
if(file.fail())
throw std::runtime_error("FileStream: failed to open file " + name);
}
~FileStream() { file.close(); }
};
typedef boost::shared_ptr<FileStream> FileStreamPtr;
}} // namespaces
#endif

@ -1,116 +0,0 @@
#ifndef MANGLE_STREAM_MEMSERVER_H
#define MANGLE_STREAM_MEMSERVER_H
#include <assert.h>
#include "../stream.hpp"
#include <string.h>
namespace Mangle {
namespace Stream {
// Do this before the class declaration, since the class itself
// uses it.
class MemoryStream;
typedef boost::shared_ptr<MemoryStream> MemoryStreamPtr;
/** A Stream wrapping a memory buffer
This will create a fully seekable stream out of any pointer/length
pair you give it.
*/
class MemoryStream : public Stream
{
const void *data;
size_t length, pos;
public:
MemoryStream(const void *ptr, size_t len)
: data(ptr), length(len), pos(0)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
hasPtr = true;
}
MemoryStream()
: data(NULL), length(0), pos(0)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
hasPtr = true;
}
size_t read(void *buf, size_t count)
{
assert(data != NULL);
assert(pos <= length);
// Don't read more than we have
if(count > (length - pos))
count = length - pos;
// Copy data
if(count)
memcpy(buf, ((char*)data)+pos, count);
// aaand remember to increase the count
pos += count;
return count;
}
void seek(size_t _pos)
{
pos = _pos;
if(pos > length)
pos = length;
}
size_t tell() const { return pos; }
size_t size() const { return length; }
bool eof() const { return pos == length; }
const void *getPtr() { return data; }
const void *getPtr(size_t size)
{
// This variant of getPtr must move the position pointer
size_t opos = pos;
pos += size;
if(pos > length) pos = length;
return ((char*)data)+opos;
}
const void *getPtr(size_t pos, size_t size)
{
if(pos > length) pos = length;
return ((char*)data)+pos;
}
// New members in MemoryStream:
/// Set a new buffer and length. This will rewind the position to zero.
void set(const void* _data, size_t _length)
{
data = _data;
length = _length;
pos = 0;
}
/// Clone this memory stream
/** Make a new stream of the same buffer. If setPos is true, we also
set the clone's position to be the same as ours.
No memory is copied during this operation, the new stream is
just another 'view' into the same shared memory buffer.
*/
MemoryStreamPtr clone(bool setPos=false) const
{
MemoryStreamPtr res(new MemoryStream(data, length));
if(setPos) res->seek(pos);
return res;
}
};
}} // namespaces
#endif

@ -1,37 +0,0 @@
#ifndef MANGLE_STREAM_OGRESERVER_H
#define MANGLE_STREAM_OGRESERVER_H
#include <OgreDataStream.h>
namespace Mangle {
namespace Stream {
/** A Stream wrapping an OGRE DataStream.
This has been built and tested against OGRE 1.6.2. You might have
to make your own modifications if you're working with newer (or
older) versions.
*/
class OgreStream : public Stream
{
Ogre::DataStreamPtr inp;
public:
OgreStream(Ogre::DataStreamPtr _inp) : inp(_inp)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
}
size_t read(void *buf, size_t count) { return inp->read(buf,count); }
void seek(size_t pos) { inp->seek(pos); }
size_t tell() const { return inp->tell(); }
size_t size() const { return inp->size(); }
bool eof() const { return inp->eof(); }
};
typedef boost::shared_ptr<OgreStream> OgreStreamPtr;
}} // namespaces
#endif

@ -1,41 +0,0 @@
#ifndef MANGLE_OSTREAM_FILESERVER_H
#define MANGLE_OSTREAM_FILESERVER_H
#include "std_ostream.hpp"
#include <fstream>
namespace Mangle {
namespace Stream {
/** File stream based on std::ofstream, only supports writing.
*/
class OutFileStream : public StdOStream
{
std::ofstream file;
public:
/**
By default we overwrite the file. If append=true, then we will
open an existing file and seek to the end instead.
*/
OutFileStream(const std::string &name, bool append=false)
: StdOStream(&file)
{
std::ios::openmode mode = std::ios::binary;
if(append)
mode |= std::ios::app;
else
mode |= std::ios::trunc;
file.open(name.c_str(), mode);
if(file.fail())
throw std::runtime_error("OutFileStream: failed to open file " + name);
}
~OutFileStream() { file.close(); }
};
typedef boost::shared_ptr<OutFileStream> OutFileStreamPtr;
}} // namespaces
#endif

@ -1,36 +0,0 @@
#ifndef MANGLE_STREAM_OGRESERVER_H
#define MANGLE_STREAM_OGRESERVER_H
#include <physfs.h>
namespace Mangle {
namespace Stream {
/// A Stream wrapping a PHYSFS_file stream from the PhysFS library.
class PhysFile : public Stream
{
PHYSFS_file *file;
public:
PhysFile(PHYSFS_file *inp) : file(inp)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
}
~PhysFile() { PHYSFS_close(file); }
size_t read(void *buf, size_t count)
{ return PHYSFS_read(file, buf, 1, count); }
void seek(size_t pos) { PHYSFS_seek(file, pos); }
size_t tell() const { return PHYSFS_tell(file); }
size_t size() const { return PHYSFS_fileLength(file); }
bool eof() const { return PHYSFS_eof(file); }
};
typedef boost::shared_ptr<PhysFile> PhysFilePtr;
}} // namespaces
#endif

@ -1,78 +0,0 @@
#ifndef MANGLE_OSTREAM_STDIOSERVER_H
#define MANGLE_OSTREAM_STDIOSERVER_H
#include "../stream.hpp"
#include <iostream>
#include <stdexcept>
namespace Mangle {
namespace Stream {
/** Simple wrapper for std::ostream, only supports output.
*/
class StdOStream : public Stream
{
std::ostream *inf;
static void fail(const std::string &msg)
{ throw std::runtime_error("StdOStream: " + msg); }
public:
StdOStream(std::ostream *_inf)
: inf(_inf)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
isWritable = true;
isReadable = false;
}
size_t write(const void* buf, size_t len)
{
inf->write((const char*)buf, len);
if(inf->fail())
fail("error writing to stream");
// Just return len, but that is ok. The only cases where we would
// return less than len is when an error occured.
return len;
}
void flush()
{
inf->flush();
}
void seek(size_t pos)
{
inf->seekp(pos);
if(inf->fail())
fail("seek error");
}
size_t tell() const
// Hack around the fact that ifstream->tellp() isn't const
{ return ((StdOStream*)this)->inf->tellp(); }
size_t size() const
{
// Use the standard iostream size hack, terrible as it is.
std::streampos pos = inf->tellp();
inf->seekp(0, std::ios::end);
size_t res = inf->tellp();
inf->seekp(pos);
if(inf->fail())
fail("could not get stream size");
return res;
}
bool eof() const
{ return inf->eof(); }
};
typedef boost::shared_ptr<StdOStream> StdOStreamPtr;
}} // namespaces
#endif

@ -1,70 +0,0 @@
#ifndef MANGLE_STREAM_STDIOSERVER_H
#define MANGLE_STREAM_STDIOSERVER_H
#include "../stream.hpp"
#include <iostream>
#include <stdexcept>
namespace Mangle {
namespace Stream {
/** Simple wrapper for std::istream.
*/
class StdStream : public Stream
{
std::istream *inf;
static void fail(const std::string &msg)
{ throw std::runtime_error("StdStream: " + msg); }
public:
StdStream(std::istream *_inf)
: inf(_inf)
{
isSeekable = true;
hasPosition = true;
hasSize = true;
}
size_t read(void* buf, size_t len)
{
inf->read((char*)buf, len);
if(inf->bad())
fail("error reading from stream");
return inf->gcount();
}
void seek(size_t pos)
{
inf->clear();
inf->seekg(pos);
if(inf->fail())
fail("seek error");
}
size_t tell() const
// Hack around the fact that ifstream->tellg() isn't const
{ return ((StdStream*)this)->inf->tellg(); }
size_t size() const
{
// Use the standard iostream size hack, terrible as it is.
std::streampos pos = inf->tellg();
inf->seekg(0, std::ios::end);
size_t res = inf->tellg();
inf->seekg(pos);
if(inf->fail())
fail("could not get stream size");
return res;
}
bool eof() const
{ return inf->eof(); }
};
typedef boost::shared_ptr<StdStream> StdStreamPtr;
}} // namespaces
#endif

@ -1,103 +0,0 @@
#ifndef MANGLE_STREAM_INPUT_H
#define MANGLE_STREAM_INPUT_H
#include <stdlib.h>
#include "../tools/shared_ptr.hpp"
#include <assert.h>
namespace Mangle {
namespace Stream {
/// An abstract interface for a stream data.
class Stream
{
public:
// Feature options. These should be set in the constructor.
/// If true, seek() works
bool isSeekable;
/// If true, tell() works
bool hasPosition;
/// If true, size() works
bool hasSize;
/// If true, write() works. Writing through pointer operations is
/// not (yet) supported.
bool isWritable;
/// If true, read() and eof() works.
bool isReadable;
/// If true, the getPtr() functions work
bool hasPtr;
/// Initialize all bools to false by default, except isReadable.
Stream() :
isSeekable(false), hasPosition(false), hasSize(false),
isWritable(false), isReadable(true), hasPtr(false) {}
/// Virtual destructor
virtual ~Stream() {}
/** Read a given number of bytes from the stream. Returns the actual
number read. If the return value is less than count, then the
stream is empty or an error occured. Only required for readable
streams.
*/
virtual size_t read(void* buf, size_t count) { assert(0); return 0; }
/** Write a given number of bytes from the stream. Semantics is
similar to read(). Only valid if isWritable is true.
The returned value is the number of bytes written. However in
most cases, unlike for read(), a write-count less than requested
usually indicates an error. The implementation should throw such
errors as exceptions rather than expect the caller to handle
them.
Since most implementations do NOT support writing we default to
an assert(0) here.
*/
virtual size_t write(const void *buf, size_t count) { assert(0); return 0; }
/// Flush an output stream. Does nothing for non-writing streams.
virtual void flush() {}
/// Seek to an absolute position in this stream. Not all streams are
/// seekable.
virtual void seek(size_t pos) { assert(0); }
/// Get the current position in the stream. Non-seekable streams are
/// not required to keep track of this.
virtual size_t tell() const { assert(0); return 0; }
/// Return the total size of the stream. For streams hasSize is
/// false, size() should fail in some way, since it is an error to
/// call it in those cases.
virtual size_t size() const { assert(0); return 0; }
/// Returns true if the stream is empty. Required for readable
/// streams.
virtual bool eof() const { assert(0); return 0; }
/// Return a pointer to the entire stream. This function (and the
/// other getPtr() variants below) should only be implemented for
/// memory-based streams where using them would be an optimization.
virtual const void *getPtr() { assert(0); return NULL; }
/// Get a pointer to a memory region of 'size' bytes starting from
/// position 'pos'
virtual const void *getPtr(size_t pos, size_t size) { assert(0); return NULL; }
/// Get a pointer to a memory region of 'size' bytes from the
/// current position. Unlike the two other getPtr variants, this
/// will advance the position past the returned area.
virtual const void *getPtr(size_t size) { assert(0); return NULL; }
};
typedef boost::shared_ptr<Stream> StreamPtr;
}} // namespaces
#endif

@ -1,2 +0,0 @@
*_test
test.file

@ -1,34 +0,0 @@
GCC=g++ -I../ -Wall -Werror
all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test
I_OGRE=$(shell pkg-config --cflags OGRE)
L_OGRE=$(shell pkg-config --libs OGRE)
L_AUDIERE=-laudiere
ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.hpp
$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE)
audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp
$(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE)
iostream_test: iostream_test.cpp ../clients/io_stream.cpp
$(GCC) $^ -o $@
file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
$(GCC) $< -o $@
file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp
$(GCC) $< -o $@
memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp
$(GCC) $< -o $@
buffer_filter_test: buffer_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/buffer_stream.hpp
$(GCC) $< -o $@
slice_filter_test: slice_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/slice_stream.hpp
$(GCC) $< -o $@
clean:
rm *_test

@ -1,34 +0,0 @@
#include "../servers/memory_stream.hpp"
#include "../clients/audiere_file.hpp"
#include <audiere.h>
#include <iostream>
using namespace Mangle::Stream;
using namespace audiere;
using namespace std;
int main()
{
char str[12];
memset(str, 0, 12);
StreamPtr inp(new MemoryStream("hello world", 11));
FilePtr p(new AudiereFile(inp));
cout << "pos=" << p->tell() << endl;
p->read(str, 2);
cout << "2 bytes: " << str << endl;
cout << "pos=" << p->tell() << endl;
p->seek(4, File::BEGIN);
cout << "pos=" << p->tell() << endl;
p->read(str, 3);
cout << "3 bytes: " << str << endl;
p->seek(-1, File::CURRENT);
cout << "pos=" << p->tell() << endl;
p->seek(-4, File::END);
cout << "pos=" << p->tell() << endl;
p->read(str, 4);
cout << "last 4 bytes: " << str << endl;
p->seek(0, File::BEGIN);
p->read(str, 11);
cout << "entire stream: " << str << endl;
return 0;
}

@ -1,41 +0,0 @@
#include <iostream>
#include <string.h>
#include "../filters/buffer_stream.hpp"
using namespace Mangle::Stream;
using namespace std;
int main()
{
StreamPtr orig (new MemoryStream("hello world", 11));
StreamPtr inp (new BufferStream(orig));
cout << "Size: " << inp->size() << endl;
cout << "Pos: " << inp->tell() << "\nSeeking...\n";
inp->seek(3);
cout << "Pos: " << inp->tell() << endl;
char data[12];
memset(data, 0, 12);
cout << "Reading: " << inp->read(data, 4) << endl;
cout << "Four bytes: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << "\nSeeking again...\n";
inp->seek(33);
cout << "Pos: " << inp->tell() << endl;
cout << "Eof: " << inp->eof() << "\nSeek to 6\n";
inp->seek(6);
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
cout << "Over-reading: " << inp->read(data, 200) << endl;
cout << "Result: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
inp->seek(0);
cout << "Finally, reading the entire string: " << inp->read(data,11) << endl;
cout << "Result: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
return 0;
}

@ -1,18 +0,0 @@
#include "../servers/file_stream.hpp"
#include <iostream>
using namespace Mangle::Stream;
using namespace std;
int main()
{
StreamPtr inp(new FileStream("file_server_test.cpp"));
char buf[21];
buf[20] = 0;
cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl;
inp->read(buf, 20);
cout << "First 20 bytes: " << buf << endl;
cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl;
return 0;
}

@ -1,41 +0,0 @@
#include "../servers/outfile_stream.hpp"
#include <iostream>
using namespace Mangle::Stream;
using namespace std;
void print(Stream &str)
{
cout << "size=" << str.size()
<< " pos=" << str.tell()
<< " eof=" << str.eof()
<< endl;
}
int main()
{
{
cout << "\nCreating file\n";
OutFileStream out("test.file");
print(out);
out.write("hello",5);
print(out);
}
{
cout << "\nAppending to file\n";
OutFileStream out("test.file", true);
print(out);
out.write(" again\n",7);
print(out);
}
{
cout << "\nOverwriting file\n";
OutFileStream out("test.file");
print(out);
out.write("overwrite!\n",11);
print(out);
}
return 0;
}

@ -1,176 +0,0 @@
#include <iostream>
#include "../clients/io_stream.hpp"
#include "../servers/memory_stream.hpp"
using namespace Mangle::Stream;
using namespace std;
void test1()
{
cout << "Testing ASCII reading from memory:\n";
StreamPtr input(new MemoryStream("hello you world you", 19));
MangleIStream inp(input);
string str;
while(!inp.eof())
{
inp >> str;
cout << "Got: " << str << endl;
}
}
class Dummy : public Stream
{
int count;
public:
Dummy() : count(0)
{
}
size_t read(void *ptr, size_t num)
{
char *p = (char*)ptr;
char *start = p;
for(; (count < 2560) && (p-start < (int)num); count++)
{
*p = count / 10;
p++;
}
return p-start;
}
bool eof() const { return count == 2560; }
};
void test2()
{
cout << "\nTesting binary reading from non-memory:\n";
StreamPtr input(new Dummy);
MangleIStream inp(input);
int x = 0;
while(!inp.eof())
{
unsigned char buf[5];
inp.read((char*)buf,5);
// istream doesn't set eof() until we read _beyond_ the end of
// the stream, so we need an extra check.
if(inp.gcount() == 0) break;
/*
for(int i=0;i<5;i++)
cout << (int)buf[i] << " ";
cout << endl;
*/
assert(buf[4] == buf[0]);
assert(buf[0] == x/2);
x++;
}
cout << " Done\n";
}
struct Dummy2 : Stream
{
Dummy2()
{
isWritable = true;
isReadable = false;
}
size_t write(const void *ptr, size_t num)
{
const char *p = (const char*)ptr;
cout << " Got: ";
for(unsigned i=0;i<num;i++)
cout << *(p++) << " ";
cout << endl;
return num;
}
};
void test3()
{
cout << "\nWriting to dummy stream:\n";
cout << " Pure dummy test:\n";
StreamPtr output(new Dummy2);
output->write("testing", 7);
cout << " Running through MangleOStream:\n";
MangleOStream out(output);
out << "hello";
out << " - are you ok?";
cout << " Flushing:\n";
out.flush();
cout << " Writing a hell of a lot of characters:\n";
for(int i=0; i<127; i++)
out << "xxxxxxxx"; // 127 * 8 = 1016
out << "fffffff"; // +7 = 1023
cout << " Just one more:\n";
out << "y";
cout << " And oooone more:\n";
out << "z";
cout << " Flushing again:\n";
out.flush();
cout << " Writing some more and exiting:\n";
out << "blah bleh blob";
}
struct Dummy3 : Stream
{
int pos;
Dummy3() : pos(0)
{
hasPosition = true;
isSeekable = true;
}
size_t read(void*, size_t num)
{
cout << " Reading " << num << " bytes from " << pos << endl;
pos += num;
return num;
}
void seek(size_t npos) { pos = npos; }
size_t tell() const { return pos; }
};
void test4()
{
cout << "\nTesting seeking;\n";
StreamPtr input(new Dummy3);
cout << " Direct reading:\n";
input->read(0,10);
input->read(0,5);
MangleIStream inp(input);
cout << " Reading from istream:\n";
char buf[20];
inp.read(buf, 20);
inp.read(buf, 20);
inp.read(buf, 20);
cout << " Seeking to 30 and reading again:\n";
inp.seekg(30);
inp.read(buf, 20);
}
int main()
{
test1();
test2();
test3();
test4();
return 0;
}

@ -1,42 +0,0 @@
#include <iostream>
#include <string.h>
#include "../servers/memory_stream.hpp"
using namespace Mangle::Stream;
using namespace std;
int main()
{
Stream* inp = new MemoryStream("hello world\0", 12);
cout << "Size: " << inp->size() << endl;
cout << "Pos: " << inp->tell() << "\nSeeking...\n";
inp->seek(3);
cout << "Pos: " << inp->tell() << endl;
char data[12];
memset(data, 0, 12);
cout << "Reading: " << inp->read(data, 4) << endl;
cout << "Four bytes: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << "\nSeeking again...\n";
inp->seek(33);
cout << "Pos: " << inp->tell() << endl;
cout << "Eof: " << inp->eof() << "\nSeek to 6\n";
inp->seek(6);
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
cout << "Over-reading: " << inp->read(data, 200) << endl;
cout << "Result: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
inp->seek(0);
cout << "Finally, reading the entire string: " << inp->read(data,11) << endl;
cout << "Result: " << data << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Pos: " << inp->tell() << endl;
cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl;
return 0;
}

@ -1,22 +0,0 @@
#include "../servers/memory_stream.hpp"
#include "../clients/ogre_datastream.hpp"
#include <iostream>
using namespace Mangle::Stream;
using namespace Ogre;
using namespace std;
int main()
{
StreamPtr inp(new MemoryStream("hello world", 11));
DataStreamPtr p(new Mangle2OgreStream("hello", inp));
cout << "Name: " << p->getName() << endl;
cout << "As string: " << p->getAsString() << endl;
cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
p->seek(0);
cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
p->skip(5);
p->skip(-2);
cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
return 0;
}

@ -1,9 +0,0 @@
pos=0
2 bytes: he
pos=2
pos=4
3 bytes: o w
pos=6
pos=7
last 4 bytes: orld
entire stream: hello world

@ -1,22 +0,0 @@
Size: 11
Pos: 0
Seeking...
Pos: 3
Reading: 4
Four bytes: lo w
Eof: 0
Pos: 7
Seeking again...
Pos: 11
Eof: 1
Seek to 6
Eof: 0
Pos: 6
Over-reading: 5
Result: world
Eof: 1
Pos: 11
Finally, reading the entire string: 11
Result: hello world
Eof: 1
Pos: 11

@ -1,3 +0,0 @@
pos=0 eof=0
First 20 bytes: #include "../servers
pos=20 eof=0

@ -1,12 +0,0 @@
Creating file
size=0 pos=0 eof=0
size=5 pos=5 eof=0
Appending to file
size=5 pos=5 eof=0
size=12 pos=12 eof=0
Overwriting file
size=0 pos=0 eof=0
size=11 pos=11 eof=0

@ -1,32 +0,0 @@
Testing ASCII reading from memory:
Got: hello
Got: you
Got: world
Got: you
Testing binary reading from non-memory:
Done
Writing to dummy stream:
Pure dummy test:
Got: t e s t i n g
Running through MangleOStream:
Flushing:
Got: h e l l o - a r e y o u o k ?
Writing a hell of a lot of characters:
Just one more:
And oooone more:
Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y
Flushing again:
Got: z
Writing some more and exiting:
Got: b l a h b l e h b l o b
Testing seeking;
Direct reading:
Reading 10 bytes from 0
Reading 5 bytes from 10
Reading from istream:
Reading 1024 bytes from 15
Seeking to 30 and reading again:
Reading 1024 bytes from 30

@ -1,23 +0,0 @@
Size: 12
Pos: 0
Seeking...
Pos: 3
Reading: 4
Four bytes: lo w
Eof: 0
Pos: 7
Seeking again...
Pos: 12
Eof: 1
Seek to 6
Eof: 0
Pos: 6
Over-reading: 6
Result: world
Eof: 1
Pos: 12
Finally, reading the entire string: 11
Result: hello world
Eof: 0
Pos: 11
Entire stream from pointer: hello world

@ -1,5 +0,0 @@
Name: hello
As string: hello world
pos=11 eof=1
pos=0 eof=0
pos=3 eof=0

@ -1,36 +0,0 @@
Slice 1:
--------
Size: 6
Pos: 0
Seeking...
Reading 6 bytes
Result: hello
Pos: 6
Eof: 1
Seeking:
Pos: 2
Eof: 0
Reading 4 bytes
Result: llo
Pos: 6
Eof: 1
Entire stream as pointer: hello
Slice 2:
--------
Size: 6
Pos: 0
Seeking...
Reading 6 bytes
Result: world
Pos: 6
Eof: 1
Seeking:
Pos: 2
Eof: 0
Reading 4 bytes
Result: rld
Pos: 6
Eof: 1
Entire stream as pointer: world

@ -1,42 +0,0 @@
#include <iostream>
#include <string.h>
#include "../filters/slice_stream.hpp"
#include "../servers/memory_stream.hpp"
using namespace Mangle::Stream;
using namespace std;
void test(StreamPtr inp)
{
cout << "Size: " << inp->size() << endl;
cout << "Pos: " << inp->tell() << "\nSeeking...\n";
char data[6];
memset(data, 0, 6);
cout << "Reading " << inp->read(data, 6) << " bytes\n";
cout << "Result: " << data << endl;
cout << "Pos: " << inp->tell() << endl;
cout << "Eof: " << inp->eof() << endl;
inp->seek(2);
cout << "Seeking:\nPos: " << inp->tell() << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Reading " << inp->read(data, 6) << " bytes\n";
cout << "Result: " << data << endl;
cout << "Pos: " << inp->tell() << endl;
cout << "Eof: " << inp->eof() << endl;
cout << "Entire stream as pointer: " << (char*)inp->getPtr() << endl;
}
int main()
{
StreamPtr orig (new MemoryStream("hello\0world\0", 12));
StreamPtr slice1 (new SliceStream(orig,0,6));
StreamPtr slice2 (new SliceStream(orig,6,6));
cout << "\nSlice 1:\n--------\n";
test(slice1);
cout << "\nSlice 2:\n--------\n";
test(slice2);
return 0;
}

@ -1,18 +0,0 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -1,85 +0,0 @@
#include "ogre_archive.hpp"
#include "../../stream/clients/ogre_datastream.hpp"
using namespace Mangle::VFS;
using namespace Mangle::Stream;
Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const
{
return Ogre::DataStreamPtr(new Mangle2OgreStream
(filename, vfs->open(filename)));
}
static void fill(Ogre::FileInfoList &out, FileInfoList &in)
{
int size = in.size();
out.resize(size);
for(int i=0; i<size; i++)
{
out[i].filename = in[i].name;
out[i].basename = in[i].basename;
out[i].path = ""; // FIXME
out[i].uncompressedSize = in[i].size;
out[i].compressedSize = in[i].size;
}
}
static void fill(Ogre::StringVector &out, FileInfoList &in)
{
int size = in.size();
out.resize(size);
for(int i=0; i<size; i++)
out[i] = in[i].name;
}
Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs)
{
assert(vfs->hasList);
FileInfoListPtr lst = vfs->list("", recursive, dirs);
Ogre::StringVector *res = new Ogre::StringVector;
fill(*res, *lst);
return Ogre::StringVectorPtr(res);
}
Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs)
{
assert(vfs->hasList);
FileInfoListPtr lst = vfs->list("", recursive, dirs);
Ogre::FileInfoList *res = new Ogre::FileInfoList;
fill(*res, *lst);
return Ogre::FileInfoListPtr(res);
}
// Find functions will only work if vfs->hasFind is set.
Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern,
bool recursive,
bool dirs)
{
assert(vfs->hasFind);
FileInfoListPtr lst = vfs->find(pattern, recursive, dirs);
Ogre::StringVector *res = new Ogre::StringVector;
fill(*res, *lst);
return Ogre::StringVectorPtr(res);
}
Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern,
bool recursive,
bool dirs)
{
assert(vfs->hasFind);
FileInfoListPtr lst = vfs->find(pattern, recursive, dirs);
Ogre::FileInfoList *res = new Ogre::FileInfoList;
fill(*res, *lst);
return Ogre::FileInfoListPtr(res);
}

@ -1,59 +0,0 @@
#ifndef MANGLE_VFS_OGRECLIENT_H
#define MANGLE_VFS_OGRECLIENT_H
#include <OgreArchive.h>
#include <assert.h>
#include "../vfs.hpp"
namespace Mangle {
namespace VFS {
/** An OGRE Archive implementation that wraps a Mangle::VFS
filesystem.
This has been built and tested against OGRE 1.6.2, and has been
extended for OGRE 1.7. You might have to make your own
modifications if you're working with newer (or older) versions.
*/
class MangleArchive : public Ogre::Archive
{
VFSPtr vfs;
public:
MangleArchive(VFSPtr _vfs, const std::string &name,
const std::string &archType = "Mangle")
: Ogre::Archive(name, archType)
, vfs(_vfs)
{}
bool isCaseSensitive() const { return vfs->isCaseSensitive; }
// These do nothing. You have to load / unload the archive in the
// constructor/destructor.
void load() {}
void unload() {}
bool exists(const Ogre::String& filename)
{ return vfs->isFile(filename); }
time_t getModifiedTime(const Ogre::String& filename)
{ return vfs->stat(filename)->time; }
// With both these we support both OGRE 1.6 and 1.7
Ogre::DataStreamPtr open(const Ogre::String& filename) const;
Ogre::DataStreamPtr open(const Ogre::String& filename, bool readOnly) const
{ return open(filename); }
Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false);
Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
// Find functions will only work if vfs->hasFind is set.
Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true,
bool dirs = false);
Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern,
bool recursive = true,
bool dirs = false);
};
}} // namespaces
#endif

@ -1,61 +0,0 @@
#include "ogre_vfs.hpp"
#include "../../stream/servers/ogre_datastream.hpp"
using namespace Mangle::VFS;
using namespace Mangle::Stream;
OgreVFS::OgreVFS(const std::string &_group)
: group(_group)
{
hasList = true;
hasFind = true;
isCaseSensitive = true;
// Get the group manager once
gm = Ogre::ResourceGroupManager::getSingletonPtr();
// Use the default group if none was specified
if(group.empty())
group = gm->getWorldResourceGroupName();
}
StreamPtr OgreVFS::open(const std::string &name)
{
Ogre::DataStreamPtr data = gm->openResource(name, group);
return StreamPtr(new OgreStream(data));
}
static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs)
{
int size = in.size();
out.resize(size);
for(int i=0; i<size; i++)
{
out[i].name = in[i].filename;
out[i].basename = in[i].basename;
out[i].size = in[i].uncompressedSize;
out[i].isDir = dirs;
out[i].time = 0;
}
}
FileInfoListPtr OgreVFS::list(const std::string& dir,
bool recurse,
bool dirs) const
{
Ogre::FileInfoListPtr olist = gm->listResourceFileInfo(group, dirs);
FileInfoListPtr res(new FileInfoList);
fill(*res, *olist, dirs);
return res;
}
FileInfoListPtr OgreVFS::find(const std::string& pattern,
bool recursive,
bool dirs) const
{
Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs);
FileInfoListPtr res(new FileInfoList);
fill(*res, *olist, dirs);
return res;
}

@ -1,70 +0,0 @@
#ifndef MANGLE_VFS_OGRESERVER_H
#define MANGLE_VFS_OGRESERVER_H
#include "../vfs.hpp"
#include <OgreResourceGroupManager.h>
namespace Mangle {
namespace VFS {
/** @brief An interface into the OGRE VFS system.
This class does NOT wrap a single Ogre::Archive, but rather an
entire resource group in Ogre. You can use this class to tap into
all paths, Zip files, custom archives on so on that have been
inserted into Ogre as resource locations.
This has been built and tested against OGRE 1.6.2. You might have
to make your own modifications if you're working with newer (or
older) versions.
*/
class OgreVFS : public VFS
{
std::string group;
Ogre::ResourceGroupManager *gm;
public:
/** @brief Constructor
OGRE must be initialized (ie. you must have created an
Ogre::Root object somewhere) before calling this.
@param group Optional resource group name. If none is given,
OGRE's default (or 'World') resource group is used.
*/
OgreVFS(const std::string &_group = "");
/// Open a new data stream. Deleting the object should be enough to
/// close it.
virtual Stream::StreamPtr open(const std::string &name);
/// Check for the existence of a file
virtual bool isFile(const std::string &name) const
{ return gm->resourceExists(group, name); }
/// This doesn't work, always returns false.
virtual bool isDir(const std::string &name) const
{ return false; }
/// This doesn't work.
virtual FileInfoPtr stat(const std::string &name) const
{ return FileInfoPtr(); }
/// List all entries in a given directory. A blank dir should be
/// interpreted as a the root/current directory of the archive. If
/// dirs is true, list directories instead of files. OGRE note: The
/// ogre resource systemd does not support recursive listing of
/// files. We might make a separate filter for this later.
virtual FileInfoListPtr list(const std::string& dir = "",
bool recurse=true,
bool dirs=false) const;
/// Find files after a given pattern. Wildcards (*) are
/// supported.
virtual FileInfoListPtr find(const std::string& pattern,
bool recursive=true,
bool dirs=false) const;
};
}} // namespaces
#endif

@ -1,71 +0,0 @@
#ifndef MANGLE_VFS_PHYSFS_SERVER_H
#define MANGLE_VFS_PHYSFS_SERVER_H
#include "../vfs.hpp"
#include "../../stream/servers/phys_stream.hpp"
#include <physfs.h>
#include <assert.h>
namespace Mangle {
namespace VFS {
/** @brief An interface into the PhysFS virtual file system library
You have to set up PhysFS on your own before using this class.
*/
class PhysVFS : public VFS
{
public:
PhysVFS()
{
hasList = true;
hasFind = false;
isCaseSensitive = true;
}
/// Open a new data stream. Deleting the object should be enough to
/// close it.
virtual Stream::StreamPtr open(const std::string &name)
{ return Stream::StreamPtr(new Stream::PhysFile(PHYSFS_openRead(name.c_str()))); }
/// Check for the existence of a file
virtual bool isFile(const std::string &name) const
{ return PHYSFS_exists(name.c_str()); }
/// Checks for a directory
virtual bool isDir(const std::string &name) const
{ return PHYSFS_isDirectory(name.c_str()); }
/// This doesn't work
virtual FileInfoPtr stat(const std::string &name) const
{ assert(0); return FileInfoPtr(); }
virtual FileInfoListPtr list(const std::string& dir = "",
bool recurse=true,
bool dirs=false) const
{
char **files = PHYSFS_enumerateFiles(dir.c_str());
FileInfoListPtr lst(new FileInfoList);
// Add all teh files
int i = 0;
while(files[i] != NULL)
{
FileInfo fi;
fi.name = files[i];
fi.isDir = false;
lst->push_back(fi);
}
return lst;
}
virtual FileInfoListPtr find(const std::string& pattern,
bool recursive=true,
bool dirs=false) const
{ assert(0); }
};
}} // namespaces
#endif

@ -1,25 +0,0 @@
GCC=g++ -I../ -Wall
all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test
I_OGRE=$(shell pkg-config --cflags OGRE)
L_OGRE=$(shell pkg-config --libs OGRE)
L_PHYSFS=-lphysfs
ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.hpp ../clients/ogre_archive.hpp ../clients/ogre_archive.cpp
$(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE)
ogre_resource_test: ogre_resource_test.cpp
$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE)
ogre_server_test: ogre_server_test.cpp ../vfs.hpp ../servers/ogre_vfs.hpp ../servers/ogre_vfs.cpp
$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp
physfs_server_test: physfs_server_test.cpp ../vfs.hpp ../servers/physfs_vfs.hpp
$(GCC) $< -o $@ $(L_PHYSFS)
dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.hpp
$(GCC) $< -o $@
clean:
rm *_test

@ -1,42 +0,0 @@
#include "dummy_vfs.cpp"
#include <iostream>
#include <string.h>
using namespace std;
void print(FileInfo &inf)
{
cout << "name: " << inf.name << endl;
cout << "basename: " << inf.basename << endl;
cout << "isDir: " << inf.isDir << endl;
cout << "size: " << inf.size << endl;
cout << "time: " << inf.time << endl;
}
void print(FileInfoPtr inf) { print(*inf); }
void print(FileInfoList &lst)
{
for(unsigned i=0; i<lst.size(); i++)
print(lst[i]);
}
void print(FileInfoListPtr lst) { print(*lst); }
int main()
{
DummyVFS vfs;
cout << "Listing all files:\n";
print(vfs.list());
cout << "\nStat for single files:\n";
print(vfs.stat("file1"));
cout << endl;
print(vfs.stat("dir/file2"));
cout << endl;
print(vfs.stat("dir"));
StreamPtr inp = vfs.open("file1");
cout << "filesize: " << inp->size() << endl;
return 0;
}

@ -1,117 +0,0 @@
// This file is shared between several test programs
#include "vfs.hpp"
#include <assert.h>
#include <string.h>
#include "../../stream/servers/memory_stream.hpp"
using namespace Mangle::VFS;
using namespace Mangle::Stream;
class DummyVFS : public VFS
{
public:
DummyVFS()
{
hasFind = false;
hasList = true;
isCaseSensitive = true;
}
// We only support opening 'file1' at the moment.
StreamPtr open(const std::string &name)
{
assert(name == "file1");
return StreamPtr(new MemoryStream("hello world", 11));
}
bool isFile(const std::string &name) const
{
return (name == "file1" ||
name == "dir/file2");
}
bool isDir(const std::string &name) const
{
return name == "dir";
}
/// Get info about a single file
FileInfoPtr stat(const std::string &name) const
{
FileInfoPtr fi(new FileInfo);
fi->name = name;
fi->time = 0;
if(isFile(name))
{
if(name == "dir/file2")
{
fi->basename = "file2";
fi->size = 2;
}
else
{
fi->basename = "file1";
fi->size = 1;
}
fi->isDir = false;
}
else if(isDir(name))
{
fi->basename = "dir";
fi->isDir = true;
fi->size = 0;
}
else assert(0);
return fi;
}
/// List all entries in a given directory. A blank dir should be
/// interpreted as a the root/current directory of the archive. If
/// dirs is true, list directories instead of files.
virtual FileInfoListPtr list(const std::string& dir = "",
bool recurse=true,
bool dirs=false) const
{
assert(dir == "");
FileInfoListPtr fl(new FileInfoList);
FileInfo fi;
if(!dirs)
{
fi.name = "file1";
fi.basename = "file1";
fi.isDir = false;
fi.size = 1;
fi.time = 0;
fl->push_back(fi);
if(recurse)
{
fi.name = "dir/file2";
fi.basename = "file2";
fi.size = 2;
fl->push_back(fi);
}
}
else
{
fi.name = "dir";
fi.basename = "dir";
fi.isDir = true;
fi.size = 0;
fi.time = 0;
fl->push_back(fi);
}
return fl;
}
FileInfoListPtr find(const std::string& pattern,
bool recursive=true,
bool dirs=false) const
{ assert(0); }
};

@ -1,39 +0,0 @@
#include "dummy_vfs.cpp"
#include "../clients/ogre_archive.hpp"
#include <iostream>
using namespace Ogre;
using namespace std;
void print(StringVectorPtr lst)
{
int s = lst->size();
for(int i=0; i<s; i++)
{
cout << " " << (*lst)[i] << endl;
}
}
int main()
{
VFSPtr vfs(new DummyVFS());
MangleArchive arc(vfs, "dummy");
cout << "Case: " << arc.isCaseSensitive() << endl;
cout << "Name: " << arc.getName() << endl;
cout << "Type: " << arc.getType() << endl;
cout << "All files:\n";
print(arc.list());
cout << "Non-recursive:\n";
print(arc.list(false, false));
cout << "Dirs:\n";
print(arc.list(false, true));
DataStreamPtr file = arc.open("file1");
cout << "filesize: " << file->size() << endl;
cout << "contents: " << file->getAsString() << endl;
return 0;
}

@ -1,61 +0,0 @@
#include <iostream>
#include <OgreRoot.h>
#include <OgreResourceGroupManager.h>
/*
This isn't really a test of our implementation, but a test of using
the Ogre resource system to find files. If the Ogre interface
changes and you have to change this test, you will have to change
the servers/ogre_vfs.cpp implementation equivalently.
*/
using namespace std;
using namespace Ogre;
ResourceGroupManager *gm;
String group;
void find(const std::string &fileName)
{
cout << "\nFile: " << fileName << endl;
if(!gm->resourceExists(group, fileName))
{
cout << "Does not exist\n";
return;
}
DataStreamPtr data = gm->openResource(fileName, group);
cout << "Size: " << data->size() << endl;
cout << "First line: " << data->getLine() << "\n";
}
int main()
{
// Disable logging
new LogManager;
Log *log = LogManager::getSingleton().createLog("");
log->setDebugOutputEnabled(false);
// Set up Ogre
Root root("","","");
root.addResourceLocation("./", "FileSystem", "General");
gm = ResourceGroupManager::getSingletonPtr();
group = gm->getWorldResourceGroupName();
find("Makefile");
find("ogre_resource_test.cpp");
find("bleh");
cout << "\nAll source files:\n";
FileInfoListPtr list = gm->findResourceFileInfo(group, "*.cpp");
FileInfoList::iterator it, end;
it = list->begin();
end = list->end();
for(; it != end; it++)
cout << " " << it->filename << endl;
}

@ -1,38 +0,0 @@
#include "../servers/ogre_vfs.hpp"
#include <Ogre.h>
#include "server_common.cpp"
Ogre::Root *root;
void setupOgre()
{
using namespace Ogre;
// Disable logging
new LogManager;
Log *log = LogManager::getSingleton().createLog("");
log->setDebugOutputEnabled(false);
// Set up Root
root = new Root("","","");
// Add a zip file and the current directory
root->addResourceLocation("test.zip", "Zip", "General");
root->addResourceLocation("./", "FileSystem", "General");
}
int main()
{
// Set up the engine
setupOgre();
// This is our entry point into the resource file system
OgreVFS vfs("General");
// Run the test
testAll(vfs);
return 0;
}

@ -1,31 +0,0 @@
Listing all files:
name: file1
basename: file1
isDir: 0
size: 1
time: 0
name: dir/file2
basename: file2
isDir: 0
size: 2
time: 0
Stat for single files:
name: file1
basename: file1
isDir: 0
size: 1
time: 0
name: dir/file2
basename: file2
isDir: 0
size: 2
time: 0
name: dir
basename: dir
isDir: 1
size: 0
time: 0
filesize: 11

@ -1,12 +0,0 @@
Case: 1
Name: dummy
Type: Mangle
All files:
file1
dir/file2
Non-recursive:
file1
Dirs:
dir
filesize: 11
contents: hello world

@ -1,20 +0,0 @@
File: Makefile
Size: 828
First line: GCC=g++ -I../
File: ogre_resource_test.cpp
Size: 1437
First line: #include <iostream>
File: bleh
Does not exist
All source files:
physfs_server_test.cpp
dummy_test.cpp
ogre_resource_test.cpp
server_common.cpp
dummy_vfs.cpp
ogre_client_test.cpp
ogre_server_test.cpp

@ -1,11 +0,0 @@
File: Makefile
Size: 828
First 12 bytes: GCC=g++ -I..
File: testfile.txt
Size: 13
First 12 bytes: hello world!
File: blah_bleh
File doesn't exist

@ -1,11 +0,0 @@
File: Makefile
Size: 828
First 12 bytes: GCC=g++ -I..
File: testfile.txt
Size: 13
First 12 bytes: hello world!
File: blah_bleh
File doesn't exist

@ -1,21 +0,0 @@
#include "../servers/physfs_vfs.hpp"
#include "server_common.cpp"
#include <physfs.h>
int main()
{
// Set up the library and paths
PHYSFS_init("blah");
PHYSFS_addToSearchPath("test.zip", 1);
PHYSFS_addToSearchPath("./", 1);
// Create our interface
PhysVFS vfs;
// Run the test
testAll(vfs);
return 0;
}

@ -1,33 +0,0 @@
#include <iostream>
using namespace Mangle::VFS;
using namespace Mangle::Stream;
using namespace std;
void find(VFS &vfs, const std::string &file)
{
cout << "\nFile: " << file << endl;
if(!vfs.isFile(file))
{
cout << "File doesn't exist\n";
return;
}
StreamPtr data = vfs.open(file);
cout << "Size: " << data->size() << endl;
char buf[13];
buf[12] = 0;
data->read(buf, 12);
cout << "First 12 bytes: " << buf << "\n";
}
void testAll(VFS &vfs)
{
find(vfs, "Makefile"); // From the file system
find(vfs, "testfile.txt"); // From the zip
find(vfs, "blah_bleh"); // Doesn't exist
}

@ -1,18 +0,0 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

Binary file not shown.

@ -1,87 +0,0 @@
#ifndef MANGLE_VFS_H
#define MANGLE_VFS_H
#include "../stream/stream.hpp"
#include <string>
#include <vector>
namespace Mangle {
namespace VFS {
/// Generic file info structure
struct FileInfo
{
/// Full name, including path
std::string name;
/// Base name, not including path
std::string basename;
/// Is this a directory?
bool isDir;
/// File size
size_t size;
/// Last modification date
time_t time;
};
typedef std::vector<FileInfo> FileInfoList;
typedef boost::shared_ptr<FileInfo> FileInfoPtr;
typedef boost::shared_ptr<FileInfoList> FileInfoListPtr;
/** An interface to any file system or other provider of named data
streams
*/
class VFS
{
public:
// Feature options. These should be set in the constructor.
/// If true, the list() function work
bool hasList;
/// If true, the find() function work
bool hasFind;
/// If true, the file system is case sensitive
bool isCaseSensitive;
/// Virtual destructor
virtual ~VFS() {}
/// Open a new data stream. Deleting the object (letting all the
/// pointers to it go out of scope) should be enough to close it.
virtual Stream::StreamPtr open(const std::string &name) = 0;
/// Check for the existence of a file
virtual bool isFile(const std::string &name) const = 0;
/// Check for the existence of a directory
virtual bool isDir(const std::string &name) const = 0;
/// Get info about a single file
virtual FileInfoPtr stat(const std::string &name) const = 0;
/// List all entries in a given directory. A blank dir should be
/// interpreted as a the root/current directory of the archive. If
/// dirs is true, list directories instead of files.
virtual FileInfoListPtr list(const std::string& dir = "",
bool recurse=true,
bool dirs=false) const = 0;
/// Find files after a given pattern. Wildcards (*) are
/// supported. Only valid if 'hasFind' is true. Don't implement your
/// own pattern matching here if the backend doesn't support it
/// natively; use a filter instead.
virtual FileInfoListPtr find(const std::string& pattern,
bool recursive=true,
bool dirs=false) const = 0;
};
typedef boost::shared_ptr<VFS> VFSPtr;
}} // namespaces
#endif
Loading…
Cancel
Save