Commit b410b526 authored by hybrid's avatar hybrid

PAK archive reader patch and test, submitted by Carmen Wick.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2741 dfc29bdd-3216-0410-991c-e03cc46cb475
parent d3eff725
...@@ -15,20 +15,34 @@ namespace irr ...@@ -15,20 +15,34 @@ namespace irr
namespace io namespace io
{ {
namespace
{
inline bool isHeaderValid(const SPAKFileHeader& header)
{
const c8* tag = header.tag;
return tag[0] == 'P' &&
tag[1] == 'A' &&
tag[2] == 'C' &&
tag[3] == 'K';
}
} // end namespace
//! Constructor //! Constructor
CArchiveLoaderPAK::CArchiveLoaderPAK( io::IFileSystem* fs) CArchiveLoaderPAK::CArchiveLoaderPAK( io::IFileSystem* fs)
: FileSystem(fs) : FileSystem(fs)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CArchiveLoaderPAK"); setDebugName("CArchiveLoaderPAK");
#endif #endif
} }
//! returns true if the file maybe is able to be loaded by this class //! returns true if the file maybe is able to be loaded by this class
bool CArchiveLoaderPAK::isALoadableFileFormat(const io::path& filename) const bool CArchiveLoaderPAK::isALoadableFileFormat(const io::path& filename) const
{ {
return core::hasFileExtension ( filename, "pak" ); return core::hasFileExtension(filename, "pak");
} }
//! Check to see if the loader can create archives of this type. //! Check to see if the loader can create archives of this type.
...@@ -47,7 +61,7 @@ IFileArchive* CArchiveLoaderPAK::createArchive(const io::path& filename, bool ig ...@@ -47,7 +61,7 @@ IFileArchive* CArchiveLoaderPAK::createArchive(const io::path& filename, bool ig
if (file) if (file)
{ {
archive = createArchive ( file, ignoreCase, ignorePaths ); archive = createArchive(file, ignoreCase, ignorePaths);
file->drop (); file->drop ();
} }
...@@ -76,9 +90,9 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const ...@@ -76,9 +90,9 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const
{ {
SPAKFileHeader header; SPAKFileHeader header;
file->read( &header.tag, 4 ); file->read(&header, sizeof(header));
return header.tag[0] == 'P' && header.tag[1] == 'A'; return isHeaderValid(header);
} }
...@@ -88,17 +102,14 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const ...@@ -88,17 +102,14 @@ bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const
CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths) CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
: CFileList(file ? file->getFileName() : "", ignoreCase, ignorePaths), File(file) : CFileList(file ? file->getFileName() : "", ignoreCase, ignorePaths), File(file)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CPakReader"); setDebugName("CPakReader");
#endif #endif
if (File) if (File)
{ {
File->grab(); File->grab();
// scan local headers
scanLocalHeader(); scanLocalHeader();
sort(); sort();
} }
} }
...@@ -116,48 +127,43 @@ const IFileList* CPakReader::getFileList() const ...@@ -116,48 +127,43 @@ const IFileList* CPakReader::getFileList() const
return this; return this;
} }
//! scans for a local header, returns false if there is no more local file header.
bool CPakReader::scanLocalHeader() bool CPakReader::scanLocalHeader()
{ {
SPAKFileHeader header;
c8 tmp[1024]; // Read and validate the header
io::path PakFileName; File->read(&header, sizeof(header));
if (!isHeaderValid(header))
memset(&header, 0, sizeof(SPAKFileHeader)); return false;
File->read(&header, sizeof(SPAKFileHeader));
if (header.tag[0] != 'P' && header.tag[1] != 'A')
return false; // local file headers end here.
// Seek to the table of contents
#ifdef __BIG_ENDIAN__
header.offset = os::Byteswap::byteswap(header.offset);
header.length = os::Byteswap::byteswap(header.length);
#endif
File->seek(header.offset); File->seek(header.offset);
const int count = header.length / ((sizeof(u32) * 2) + 56); const int numberOfFiles = header.length / sizeof(SPAKFileEntry);
for(int i = 0; i < count; i++) Offsets.reallocate(numberOfFiles);
// Loop through each entry in the table of contents
for(int i = 0; i < numberOfFiles; i++)
{ {
// read filename // read an entry
PakFileName.reserve(56+2); SPAKFileEntry entry;
File->read(tmp, 56); File->read(&entry, sizeof(entry));
tmp[56] = 0x0;
PakFileName = tmp;
#ifdef _DEBUG
os::Printer::log(PakFileName.c_str());
#endif
s32 offset; #ifdef _DEBUG
s32 size; os::Printer::log(entry.name);
#endif
File->read(&offset, sizeof(u32));
File->read(&size, sizeof(u32));
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(offset); entry.offset = os::Byteswap::byteswap(entry.offset);
os::Byteswap::byteswap(size); entry.length = os::Byteswap::byteswap(entry.length);
#endif #endif
addItem(PakFileName, size, false, Offsets.size()); addItem(io::path(entry.name), entry.length, false, Offsets.size());
Offsets.push_back(offset); Offsets.push_back(entry.offset);
} }
return true; return true;
} }
......
...@@ -20,13 +20,24 @@ namespace irr ...@@ -20,13 +20,24 @@ namespace irr
{ {
namespace io namespace io
{ {
//! File header containing location and size of the table of contents
struct SPAKFileHeader struct SPAKFileHeader
{ {
// Don't change the order of these fields! They must match the order stored on disk.
c8 tag[4]; c8 tag[4];
u32 offset; u32 offset;
u32 length; u32 length;
}; };
//! An entry in the PAK file's table of contents.
struct SPAKFileEntry
{
// Don't change the order of these fields! They must match the order stored on disk.
c8 name[56];
u32 offset;
u32 length;
};
//! Archiveloader capable of loading PAK Archives //! Archiveloader capable of loading PAK Archives
class CArchiveLoaderPAK : public IArchiveLoader class CArchiveLoaderPAK : public IArchiveLoader
{ {
...@@ -79,7 +90,6 @@ namespace io ...@@ -79,7 +90,6 @@ namespace io
// file archive methods // file archive methods
//! return the id of the file Archive //! return the id of the file Archive
virtual const io::path& getArchiveName() const virtual const io::path& getArchiveName() const
{ {
return File->getFileName(); return File->getFileName();
...@@ -99,16 +109,11 @@ namespace io ...@@ -99,16 +109,11 @@ namespace io
private: private:
//! scans for a local header, returns false if there is no more local file header. //! scans for a local header, returns false if the header is invalid
bool scanLocalHeader(); bool scanLocalHeader();
//! splits filename from zip file into useful filenames and paths
//void extractFilename(SPakFileEntry* entry);
IReadFile* File; IReadFile* File;
SPAKFileHeader header;
//! Contains offsets of the files from the start of the archive file //! Contains offsets of the files from the start of the archive file
core::array<u32> Offsets; core::array<u32> Offsets;
}; };
......
...@@ -58,6 +58,7 @@ int main(int argumentCount, char * arguments[]) ...@@ -58,6 +58,7 @@ int main(int argumentCount, char * arguments[])
TEST(disambiguateTextures); // Normally you should run this first, since it validates the working directory. TEST(disambiguateTextures); // Normally you should run this first, since it validates the working directory.
TEST(filesystem); TEST(filesystem);
TEST(zipReader); TEST(zipReader);
TEST(pakReader);
TEST(exports); TEST(exports);
TEST(sceneCollisionManager); TEST(sceneCollisionManager);
TEST(testVector3d); TEST(testVector3d);
......
#include "testUtils.h"
using namespace irr;
using namespace core;
using namespace io;
bool pakReader(void)
{
IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(1, 1));
assert(device);
if(!device)
return false;
io::IFileSystem * fs = device->getFileSystem ();
if ( !fs )
return false;
if ( !fs->addFileArchive(io::path("media/sample_pakfile.pak"), /*bool ignoreCase=*/true, /*bool ignorePaths=*/false) )
return false;
// log what we got
io::IFileArchive* archive = fs->getFileArchive(fs->getFileArchiveCount()-1);
const io::IFileList* fileList = archive->getFileList();
for ( u32 f=0; f < fileList->getFileCount(); ++f)
{
logTestString("File name: %s\n", fileList->getFileName(f).c_str());
logTestString("Full path: %s\n", fileList->getFullFileName(f).c_str());
}
bool result = true;
io::path filename("test/test.txt");
result &= fs->existFile(filename);
if (!result )
{
logTestString("existFile failed");
}
IReadFile* readFile = fs->createAndOpenFile(filename);
if ( !readFile )
{
result = false;
logTestString("createAndOpenFilefailed");
}
char tmp[123] = {'\0'};
readFile->read(tmp, sizeof(tmp));
if (strncmp(tmp, "Hello world!", sizeof(tmp)))
{
result = false;
logTestString("Read bad data from pak file.\n");
}
return result;
}
Test suite pass at GMT Tue Oct 20 16:15:59 2009 Test suite pass at GMT Fri Oct 23 12:34:07 2009
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment