Avoid Mangle for BSA accesses

The way it was set up was not very efficient, and we're using Ogre for resource
management anyway, so it's best to just use that.
actorid
Chris Robinson 13 years ago
parent 94c3fb81d1
commit a8ebb39883

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

@ -23,17 +23,95 @@
#include "bsa_file.hpp" #include "bsa_file.hpp"
#include <libs/mangle/stream/servers/file_stream.hpp>
#include <libs/mangle/stream/filters/slice_stream.hpp>
#include <stdexcept> #include <stdexcept>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <OgreDataStream.h>
using namespace std; using namespace std;
using namespace Mangle::Stream;
using namespace Bsa; 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 /// Error handling
void BSAFile::fail(const string &msg) void BSAFile::fail(const string &msg)
{ {
@ -74,25 +152,27 @@ void BSAFile::readHeader()
* *
*/ */
assert(!isLoaded); assert(!isLoaded);
assert(input);
assert(input->hasSize); std::ifstream input(filename.c_str(), std::ios_base::binary);
assert(input->hasPosition);
assert(input->isSeekable);
// Total archive size // Total archive size
size_t fsize = input->size(); size_t fsize = 0;
if(input.seekg(0, std::ios_base::end))
{
fsize = input.tellg();
input.seekg(0);
}
if( fsize < 12 ) if(fsize < 12)
fail("File too small to be a valid BSA archive"); fail("File too small to be a valid BSA archive");
// Get essential header numbers // Get essential header numbers
size_t dirsize, filenum; size_t dirsize, filenum;
{ {
// First 12 bytes // First 12 bytes
uint32_t head[3]; uint32_t head[3];
input->read(head, 12); input.read(reinterpret_cast<char*>(head), 12);
if(head[0] != 0x100) if(head[0] != 0x100)
fail("Unrecognized BSA header"); fail("Unrecognized BSA header");
@ -108,20 +188,19 @@ void BSAFile::readHeader()
// Each file must take up at least 21 bytes of data in the bsa. So // 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 // if files*21 overflows the file size then we are guaranteed that
// the archive is corrupt. // the archive is corrupt.
if( (filenum*21 > fsize -12) || if((filenum*21 > fsize -12) || (dirsize+8*filenum > fsize -12) )
(dirsize+8*filenum > fsize -12) )
fail("Directory information larger than entire archive"); fail("Directory information larger than entire archive");
// Read the offset info into a temporary buffer // Read the offset info into a temporary buffer
vector<uint32_t> offsets(3*filenum); vector<uint32_t> offsets(3*filenum);
input->read(&offsets[0], 12*filenum); input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
// Read the string table // Read the string table
stringBuf.resize(dirsize-12*filenum); stringBuf.resize(dirsize-12*filenum);
input->read(&stringBuf[0], stringBuf.size()); input.read(&stringBuf[0], stringBuf.size());
// Check our position // Check our position
assert(input->tell() == 12+dirsize); assert(input.tellg() == 12+dirsize);
// Calculate the offset of the data buffer. All file offsets are // Calculate the offset of the data buffer. All file offsets are
// relative to this. 12 header bytes + directory + hash table // relative to this. 12 header bytes + directory + hash table
@ -150,44 +229,29 @@ void BSAFile::readHeader()
/// Get the index of a given file name, or -1 if not found /// Get the index of a given file name, or -1 if not found
int BSAFile::getIndex(const char *str) const int BSAFile::getIndex(const char *str) const
{ {
Lookup::const_iterator it; Lookup::const_iterator it = lookup.find(str);
it = lookup.find(str); if(it == lookup.end())
return -1;
if(it == lookup.end()) return -1;
else
{
int res = it->second; int res = it->second;
assert(res >= 0 && res < static_cast<int> (files.size())); assert(res >= 0 && (size_t)res < files.size());
return res; return res;
}
} }
/// Open an archive file. /// Open an archive file.
void BSAFile::open(const string &file) void BSAFile::open(const string &file)
{ {
filename = file; filename = file;
input = StreamPtr(new FileStream(file));
readHeader(); readHeader();
} }
/** Open an archive from a generic stream. The 'name' parameter is Ogre::DataStreamPtr BSAFile::getFile(const char *file)
used for error messages.
*/
void BSAFile::open(StreamPtr inp, const string &name)
{
filename = name;
input = inp;
readHeader();
}
StreamPtr BSAFile::getFile(const char *file)
{ {
assert(file); assert(file);
int i = getIndex(file); int i = getIndex(file);
if(i == -1) if(i == -1)
fail("File not found: " + string(file)); fail("File not found: " + string(file));
FileStruct &fs = files[i]; const FileStruct &fs = files[i];
return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize));
return StreamPtr(new SliceStream(input, fs.offset, fs.fileSize));
} }

@ -24,13 +24,15 @@
#ifndef BSA_BSA_FILE_H #ifndef BSA_BSA_FILE_H
#define BSA_BSA_FILE_H #define BSA_BSA_FILE_H
#include <libs/mangle/stream/stream.hpp>
#include <libs/platform/stdint.h> #include <libs/platform/stdint.h>
#include <libs/platform/strings.h> #include <libs/platform/strings.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <OgreDataStream.h>
namespace Bsa namespace Bsa
{ {
@ -39,8 +41,7 @@ namespace Bsa
*/ */
class BSAFile class BSAFile
{ {
public: public:
/// Represents one file entry in the archive /// Represents one file entry in the archive
struct FileStruct struct FileStruct
{ {
@ -50,16 +51,11 @@ class BSAFile
uint32_t fileSize, offset; uint32_t fileSize, offset;
// Zero-terminated file name // Zero-terminated file name
char* name; const char *name;
}; };
typedef std::vector<FileStruct> FileList; typedef std::vector<FileStruct> FileList;
private: private:
/// The archive source
Mangle::Stream::StreamPtr input;
/// Table of files in this archive /// Table of files in this archive
FileList files; FileList files;
@ -95,31 +91,27 @@ class BSAFile
/// Get the index of a given file name, or -1 if not found /// Get the index of a given file name, or -1 if not found
int getIndex(const char *str) const; int getIndex(const char *str) const;
public: public:
/* ----------------------------------- /* -----------------------------------
* BSA management methods * BSA management methods
* ----------------------------------- * -----------------------------------
*/ */
BSAFile() BSAFile()
: input(), isLoaded(false) {} : isLoaded(false)
{ }
/// Open an archive file. /// Open an archive file.
void open(const std::string &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 * Archive file routines
* ----------------------------------- * -----------------------------------
*/ */
/// Check if a file exists /// Check if a file exists
bool exists(const char *file) const { return getIndex(file) != -1; } bool exists(const char *file) const
{ return getIndex(file) != -1; }
/** Open a file contained in the archive. Throws an exception if the /** Open a file contained in the archive. Throws an exception if the
file doesn't exist. file doesn't exist.
@ -127,7 +119,7 @@ class BSAFile
NOTE: All files opened from one archive will share a common file NOTE: All files opened from one archive will share a common file
handle. This is NOT thread safe. handle. This is NOT thread safe.
*/ */
Mangle::Stream::StreamPtr getFile(const char *file); Ogre::DataStreamPtr getFile(const char *file);
/// Get a list of all files /// Get a list of all files
const FileList &getList() const const FileList &getList() const

Loading…
Cancel
Save