Commit a7f1c19c authored by bitplane's avatar bitplane

Added gzip support to ZIP archive loader. To load a tar.gz you must load it twice, like so:

       fileSystem->addFileArchive("path/to/myArchive.tar.gz");
       fileSystem->addFileArchive("myArchive.tar");

Removed buggy and unneeded use of Byteswap in TAR loader.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2449 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 1c916937
...@@ -2,8 +2,6 @@ Changes in 1.6 ...@@ -2,8 +2,6 @@ Changes in 1.6
- IFileSystem changes: - IFileSystem changes:
- Added TAR archive loader.
- Renamed the following functions- - Renamed the following functions-
IFileArchive::getArchiveType to getType IFileArchive::getArchiveType to getType
IFileSystem::registerFileArchive to addFileArchive IFileSystem::registerFileArchive to addFileArchive
...@@ -14,6 +12,13 @@ Changes in 1.6 ...@@ -14,6 +12,13 @@ Changes in 1.6
- IFileSystem::addFileArchive takes a parameter to specify the archive type rather always using the file extension. IFileSystem::addZipFileArchive, addFolderFileArchive and addPakFileArchive now use this but these functions are now marked as deprecated. Users should now use addFileArchive instead. - IFileSystem::addFileArchive takes a parameter to specify the archive type rather always using the file extension. IFileSystem::addZipFileArchive, addFolderFileArchive and addPakFileArchive now use this but these functions are now marked as deprecated. Users should now use addFileArchive instead.
- Added TAR archive loader.
- The ZIP archive loader can now load gzip files, combined with the TAR loader this means Irrlicht now has native support for .tar.gz
Currently this must be done in two calls, for example:
fileSystem->addFileArchive("path/to/myArchive.tar.gz");
fileSystem->addFileArchive("myArchive.tar");
- Fix highlighting in IGUIEditBox where kerning pairs are used in the font. For example in future italic, OS or other custom fonts. - Fix highlighting in IGUIEditBox where kerning pairs are used in the font. For example in future italic, OS or other custom fonts.
- IOSOperator::getTextFromClipboard returns now const c8* instead of c8* - IOSOperator::getTextFromClipboard returns now const c8* instead of c8*
......
...@@ -23,16 +23,16 @@ enum EFileSystemType ...@@ -23,16 +23,16 @@ enum EFileSystemType
//! Contains the different types of archives //! Contains the different types of archives
enum E_FILE_ARCHIVE_TYPE enum E_FILE_ARCHIVE_TYPE
{ {
//! A ZIP archive //! A PKZIP or gzip archive
EFAT_ZIP = MAKE_IRR_ID('Z','I','P', 0), EFAT_ZIP = MAKE_IRR_ID('Z','I','P', 0),
//! A virtual directory //! A virtual directory
EFAT_FOLDER = MAKE_IRR_ID('f','l','d','r'), EFAT_FOLDER = MAKE_IRR_ID('f','l','d','r'),
//! A Windows PAK file //! An ID Software PAK archive
EFAT_PAK = MAKE_IRR_ID('P','A','K', 0), EFAT_PAK = MAKE_IRR_ID('P','A','K', 0),
//! A Tape ARchive file //! A Tape ARchive
EFAT_TAR = MAKE_IRR_ID('T','A','R', 0), EFAT_TAR = MAKE_IRR_ID('T','A','R', 0),
//! The type of this archive is unknown //! The type of this archive is unknown
......
...@@ -85,11 +85,6 @@ bool CArchiveLoaderTAR::isALoadableFileFormat(io::IReadFile* file) const ...@@ -85,11 +85,6 @@ bool CArchiveLoaderTAR::isALoadableFileFormat(io::IReadFile* file) const
STarHeader fHead; STarHeader fHead;
file->read(&fHead, sizeof(STarHeader)); file->read(&fHead, sizeof(STarHeader));
#ifdef __BIG_ENDIAN__
for (u32* p = (u32*)&fHead; p < &fHead + sizeof(fHead); ++p)
os::Byteswap::byteswap(*p);
#endif
u32 checksum = 0; u32 checksum = 0;
sscanf(fHead.Checksum, "%lo", &checksum); sscanf(fHead.Checksum, "%lo", &checksum);
...@@ -167,11 +162,6 @@ u32 CTarReader::populateFileList() ...@@ -167,11 +162,6 @@ u32 CTarReader::populateFileList()
// read the header // read the header
File->read(&fHead, sizeof(fHead)); File->read(&fHead, sizeof(fHead));
#ifdef __BIG_ENDIAN__
for (u32* p = (u32*)&fHead; p < &fHead + sizeof(fHead); ++p)
os::Byteswap::byteswap(*p);
#endif
// only add standard files for now // only add standard files for now
if (fHead.Link == ETLI_REGULAR_FILE || ETLI_REGULAR_FILE_OLD) if (fHead.Link == ETLI_REGULAR_FILE || ETLI_REGULAR_FILE_OLD)
{ {
......
...@@ -22,8 +22,13 @@ namespace irr ...@@ -22,8 +22,13 @@ namespace irr
namespace io namespace io
{ {
// -----------------------------------------------------------------------------
// zip loader
// -----------------------------------------------------------------------------
//! Constructor //! Constructor
CArchiveLoaderZIP::CArchiveLoaderZIP( io::IFileSystem* fs) CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem* fs)
: FileSystem(fs) : FileSystem(fs)
{ {
#ifdef _DEBUG #ifdef _DEBUG
...@@ -31,17 +36,10 @@ CArchiveLoaderZIP::CArchiveLoaderZIP( io::IFileSystem* fs) ...@@ -31,17 +36,10 @@ CArchiveLoaderZIP::CArchiveLoaderZIP( io::IFileSystem* fs)
#endif #endif
} }
//! destructor
CArchiveLoaderZIP::~CArchiveLoaderZIP()
{
}
//! 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 CArchiveLoaderZIP::isALoadableFileFormat(const core::string<c16>& filename) const bool CArchiveLoaderZIP::isALoadableFileFormat(const core::string<c16>& filename) const
{ {
return core::hasFileExtension ( filename, "zip", "pk3", "dat" ); return core::hasFileExtension(filename, "zip", "pk3");
} }
...@@ -55,8 +53,8 @@ IFileArchive* CArchiveLoaderZIP::createArchive(const core::string<c16>& filename ...@@ -55,8 +53,8 @@ IFileArchive* CArchiveLoaderZIP::createArchive(const core::string<c16>& filename
if (file) if (file)
{ {
archive = createArchive ( file, ignoreCase, ignorePaths ); archive = createArchive(file, ignoreCase, ignorePaths);
file->drop (); file->drop();
} }
return archive; return archive;
...@@ -67,10 +65,22 @@ IFileArchive* CArchiveLoaderZIP::createArchive(const core::string<c16>& filename ...@@ -67,10 +65,22 @@ IFileArchive* CArchiveLoaderZIP::createArchive(const core::string<c16>& filename
IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
{ {
IFileArchive *archive = 0; IFileArchive *archive = 0;
if ( file ) if (file)
{ {
file->seek ( 0 ); file->seek(0);
archive = new CZipReader(file, ignoreCase, ignorePaths);
u16 sig;
file->read(&sig, 2);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(sig);
#endif
file->seek(0);
bool isGZip = (sig == 0x8b1f);
archive = new CZipReader(file, ignoreCase, ignorePaths, isGZip);
} }
return archive; return archive;
} }
...@@ -87,14 +97,17 @@ bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const ...@@ -87,14 +97,17 @@ bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(header.Sig); os::Byteswap::byteswap(header.Sig);
#endif #endif
return header.Sig == 0x04034b50;
return header.Sig == 0x04034b50 || // ZIP
*((u16*)(&header.Sig)) == 0x8b1f; // gzip
} }
/* // -----------------------------------------------------------------------------
ZIP Archive // zip archive
*/ // -----------------------------------------------------------------------------
CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
: File(file), IgnoreCase(ignoreCase), IgnorePaths(ignorePaths) CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip)
: File(file), IgnoreCase(ignoreCase), IgnorePaths(ignorePaths), IsGZip(isGZip)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CZipReader"); setDebugName("CZipReader");
...@@ -107,9 +120,11 @@ CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths) ...@@ -107,9 +120,11 @@ CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
Base = File->getFileName(); Base = File->getFileName();
Base.replace ( '\\', '/' ); Base.replace ( '\\', '/' );
// scan local headers // load file entries
while (scanLocalHeader()) ; if (IsGZip)
//while (scanLocalHeader2()); while (scanGZipHeader()) { }
else
while (scanZipHeader()) { }
// prepare file index for binary search // prepare file index for binary search
FileList.sort(); FileList.sort();
...@@ -122,8 +137,6 @@ CZipReader::~CZipReader() ...@@ -122,8 +137,6 @@ CZipReader::~CZipReader()
File->drop(); File->drop();
} }
//! splits filename from zip file into useful filenames and paths //! splits filename from zip file into useful filenames and paths
void CZipReader::extractFilename(SZipFileEntry* entry) void CZipReader::extractFilename(SZipFileEntry* entry)
{ {
...@@ -219,7 +232,7 @@ bool CZipReader::scanLocalHeader2() ...@@ -219,7 +232,7 @@ bool CZipReader::scanLocalHeader2()
File->seek( temp.header.ExtraFieldLength, true); File->seek( temp.header.ExtraFieldLength, true);
} }
if (temp.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRITOR) if (temp.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRIPTOR)
{ {
// read data descriptor // read data descriptor
File->seek(sizeof(SZIPFileDataDescriptor), true ); File->seek(sizeof(SZIPFileDataDescriptor), true );
...@@ -258,7 +271,98 @@ bool CZipReader::scanLocalHeader2() ...@@ -258,7 +271,98 @@ bool CZipReader::scanLocalHeader2()
#endif #endif
//! scans for a local header, returns false if there is no more local file header. //! scans for a local header, returns false if there is no more local file header.
bool CZipReader::scanLocalHeader() //! The gzip file format seems to think that there can be multiple files in a gzip file
//! but none
bool CZipReader::scanGZipHeader()
{
SZipFileEntry entry;
entry.fileDataPosition = 0;
memset(&entry.header, 0, sizeof(SZIPFileHeader));
// read header
SGZIPMemberHeader header;
if (File->read(&header, sizeof(SGZIPMemberHeader)) == sizeof(SGZIPMemberHeader))
{
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(header.sig);
os::Byteswap::byteswap(header.time);
#endif
// check header value
if (header.sig != 0x8b1f)
return false;
// now get the file info
if (header.flags & EGZF_EXTRA_FIELDS)
{
// read lenth of extra data
u16 dataLen;
File->read(&dataLen, 2);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(dataLen);
#endif
// skip it
File->seek(dataLen, true);
}
if (header.flags & EGZF_FILE_NAME)
{
c8 c;
entry.zipFileName = "";
File->read(&c, 1);
while (c)
{
entry.zipFileName.append(c);
File->read(&c, 1);
}
}
if (header.flags & EGZF_COMMENT)
{
c8 c='a';
while (c)
File->read(&c, 1);
}
if (header.flags & EGZF_CRC16)
File->seek(2, true);
// we are now at the start of the data blocks
entry.fileDataPosition = File->getPos();
entry.header.FilenameLength = entry.zipFileName.size();
entry.simpleFileName = entry.zipFileName;
entry.header.CompressionMethod = header.compressionMethod;
entry.header.DataDescriptor.CompressedSize = (File->getSize() - 8) - File->getPos();
// seek to file end
File->seek(entry.header.DataDescriptor.CompressedSize, true);
// read CRC
File->read(&entry.header.DataDescriptor.CRC32, 4);
// read uncompressed size
File->read(&entry.header.DataDescriptor.UncompressedSize, 4);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
#endif
// now we've filled all the fields, this is just a standard deflate block
// and can be read as usual
FileList.push_back(entry);
}
// there's only one block of data in a gzip file
return false;
}
//! scans for a local header, returns false if there is no more local file header.
bool CZipReader::scanZipHeader()
{ {
//c8 tmp[1024]; //c8 tmp[1024];
...@@ -302,7 +406,7 @@ bool CZipReader::scanLocalHeader() ...@@ -302,7 +406,7 @@ bool CZipReader::scanLocalHeader()
File->seek(entry.header.ExtraFieldLength, true); File->seek(entry.header.ExtraFieldLength, true);
// if bit 3 was set, read DataDescriptor, following after the compressed data // if bit 3 was set, read DataDescriptor, following after the compressed data
if (entry.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRITOR) if (entry.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRIPTOR)
{ {
// read data descriptor // read data descriptor
File->read(&entry.header.DataDescriptor, sizeof(entry.header.DataDescriptor)); File->read(&entry.header.DataDescriptor, sizeof(entry.header.DataDescriptor));
...@@ -361,7 +465,7 @@ IReadFile* CZipReader::createAndOpenFile(u32 index) ...@@ -361,7 +465,7 @@ IReadFile* CZipReader::createAndOpenFile(u32 index)
{ {
case 0: // no compression case 0: // no compression
{ {
return createLimitReadFile( e.simpleFileName, File, e.fileDataPosition, e.header.DataDescriptor.CompressedSize); return createLimitReadFile(e.simpleFileName, File, e.fileDataPosition, e.header.DataDescriptor.CompressedSize);
} }
case 8: case 8:
{ {
...@@ -413,7 +517,6 @@ IReadFile* CZipReader::createAndOpenFile(u32 index) ...@@ -413,7 +517,6 @@ IReadFile* CZipReader::createAndOpenFile(u32 index)
inflateEnd(&stream); inflateEnd(&stream);
} }
delete[] pcData; delete[] pcData;
if (err != Z_OK) if (err != Z_OK)
...@@ -453,7 +556,7 @@ const IFileArchiveEntry* CZipReader::getFileInfo(u32 index) ...@@ -453,7 +556,7 @@ const IFileArchiveEntry* CZipReader::getFileInfo(u32 index)
//! return the id of the file Archive //! return the id of the file Archive
const core::string<c16>& CZipReader::getArchiveName () const core::string<c16>& CZipReader::getArchiveName()
{ {
return Base; return Base;
} }
...@@ -488,7 +591,8 @@ s32 CZipReader::findFile( const core::string<c16>& simpleFilename) ...@@ -488,7 +591,8 @@ s32 CZipReader::findFile( const core::string<c16>& simpleFilename)
return res; return res;
} }
// -----------------------------------------------------------------------------
// mount archive loader
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//! Constructor //! Constructor
...@@ -511,10 +615,10 @@ CArchiveLoaderMount::~CArchiveLoaderMount() ...@@ -511,10 +615,10 @@ CArchiveLoaderMount::~CArchiveLoaderMount()
bool CArchiveLoaderMount::isALoadableFileFormat(const core::string<c16>& filename) const bool CArchiveLoaderMount::isALoadableFileFormat(const core::string<c16>& filename) const
{ {
bool ret = false; bool ret = false;
core::string<c16> fname ( filename ); core::string<c16> fname(filename);
deletePathFromFilename ( fname ); deletePathFromFilename(fname);
if ( 0 == fname.size() ) if (!fname.size())
{ {
ret = true; ret = true;
} }
...@@ -528,19 +632,19 @@ IFileArchive* CArchiveLoaderMount::createArchive(const core::string<c16>& filena ...@@ -528,19 +632,19 @@ IFileArchive* CArchiveLoaderMount::createArchive(const core::string<c16>& filena
{ {
IFileArchive *archive = 0; IFileArchive *archive = 0;
EFileSystemType current = FileSystem->setFileListSystem ( FILESYSTEM_NATIVE ); EFileSystemType current = FileSystem->setFileListSystem(FILESYSTEM_NATIVE);
core::string<c16> save = FileSystem->getWorkingDirectory (); core::string<c16> save = FileSystem->getWorkingDirectory();
core::string<c16> fullPath = FileSystem->getAbsolutePath ( filename ); core::string<c16> fullPath = FileSystem->getAbsolutePath(filename);
FileSystem->flattenFilename ( fullPath ); FileSystem->flattenFilename(fullPath);
if ( FileSystem->changeWorkingDirectoryTo ( fullPath ) ) if ( FileSystem->changeWorkingDirectoryTo ( fullPath ) )
{ {
archive = new CMountPointReader(FileSystem, fullPath, ignoreCase, ignorePaths); archive = new CMountPointReader(FileSystem, fullPath, ignoreCase, ignorePaths);
} }
FileSystem->changeWorkingDirectoryTo ( save ); FileSystem->changeWorkingDirectoryTo(save);
FileSystem->setFileListSystem ( current ); FileSystem->setFileListSystem(current);
return archive; return archive;
} }
...@@ -563,6 +667,10 @@ IFileArchive* CArchiveLoaderMount::createArchive(io::IReadFile* file, bool ignor ...@@ -563,6 +667,10 @@ IFileArchive* CArchiveLoaderMount::createArchive(io::IReadFile* file, bool ignor
#if 1 #if 1
// -----------------------------------------------------------------------------
// mount point reader
// -----------------------------------------------------------------------------
//! simple Reader ( does not handle ignorecase and ignorePath ) //! simple Reader ( does not handle ignorecase and ignorePath )
// its more a simple wrapper for handling relative directories // its more a simple wrapper for handling relative directories
// advantage: speed // advantage: speed
...@@ -595,7 +703,7 @@ CMountPointReader::CMountPointReader( IFileSystem * parent, const core::string<c ...@@ -595,7 +703,7 @@ CMountPointReader::CMountPointReader( IFileSystem * parent, const core::string<c
Base.append ( '/' ); Base.append ( '/' );
} }
void CMountPointReader::buildDirectory ( ) void CMountPointReader::buildDirectory()
{ {
} }
......
...@@ -20,7 +20,7 @@ namespace io ...@@ -20,7 +20,7 @@ namespace io
const s16 ZIP_FILE_ENCRYPTED = 0x0001; const s16 ZIP_FILE_ENCRYPTED = 0x0001;
// the fields crc-32, compressed size and uncompressed size are set to // the fields crc-32, compressed size and uncompressed size are set to
// zero in the local header // zero in the local header
const s16 ZIP_INFO_IN_DATA_DESCRITOR = 0x0008; const s16 ZIP_INFO_IN_DATA_DESCRIPTOR = 0x0008;
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) #if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( push, packing ) # pragma pack( push, packing )
...@@ -94,6 +94,25 @@ namespace io ...@@ -94,6 +94,25 @@ namespace io
// zipfile comment (variable size) // zipfile comment (variable size)
} PACK_STRUCT; } PACK_STRUCT;
enum E_GZIP_FLAGS
{
EGZF_TEXT_DAT = 1,
EGZF_CRC16 = 2,
EGZF_EXTRA_FIELDS = 4,
EGZF_FILE_NAME = 8,
EGZF_COMMENT = 16
};
struct SGZIPMemberHeader
{
u16 sig; // 0x8b1f
u8 compressionMethod; // 8 = deflate
u8 flags;
u32 time;
u8 extraFlags; // slow compress = 2, fast compress = 4
u8 operatingSystem;
} PACK_STRUCT;
// Default alignment // Default alignment
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) #if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop, packing ) # pragma pack( pop, packing )
...@@ -120,9 +139,6 @@ namespace io ...@@ -120,9 +139,6 @@ namespace io
//! Constructor //! Constructor
CArchiveLoaderZIP(io::IFileSystem* fs); CArchiveLoaderZIP(io::IFileSystem* fs);
//! destructor
virtual ~CArchiveLoaderZIP();
//! 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
//! based on the file extension (e.g. ".zip") //! based on the file extension (e.g. ".zip")
virtual bool isALoadableFileFormat(const core::string<c16>& filename) const; virtual bool isALoadableFileFormat(const core::string<c16>& filename) const;
...@@ -151,14 +167,15 @@ namespace io ...@@ -151,14 +167,15 @@ namespace io
/*! /*!
Zip file Reader written April 2002 by N.Gebhardt. Zip file Reader written April 2002 by N.Gebhardt.
Doesn't decompress data, only reads the file and is able to
open uncompressed entries.
*/ */
class CZipReader : public IFileArchive class CZipReader : public IFileArchive
{ {
public: public:
CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths); //! constructor
CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip=false);
//! destructor
virtual ~CZipReader(); virtual ~CZipReader();
//! opens a file by file name //! opens a file by file name
...@@ -177,33 +194,32 @@ namespace io ...@@ -177,33 +194,32 @@ namespace io
virtual s32 findFile(const core::string<c16>& filename); virtual s32 findFile(const core::string<c16>& filename);
//! return the id of the file Archive //! return the id of the file Archive
virtual const core::string<c16>& getArchiveName (); virtual const core::string<c16>& getArchiveName();
//! get the class Type //! get the class Type
virtual E_FILE_ARCHIVE_TYPE getType() const { return EFAT_ZIP; } virtual E_FILE_ARCHIVE_TYPE getType() const { return EFAT_ZIP; }
private:
//! scans for a local header, returns false if there is no more
//! local file header.
bool scanLocalHeader();
bool scanLocalHeader2();
IReadFile* File;
SZipFileEntry temp;
protected: protected:
IReadFile* File;
//! reads the next file header from a ZIP file, returns false if there are no more headers.
bool scanZipHeader();
//! the same but for gzip files
bool scanGZipHeader();
//! splits filename from zip file into useful filenames and paths //! splits filename from zip file into useful filenames and paths
void extractFilename(SZipFileEntry* entry); void extractFilename(SZipFileEntry* entry);
bool IgnoreCase; bool IgnoreCase;
bool IgnorePaths; bool IgnorePaths;
bool IsGZip;
core::array<SZipFileEntry> FileList; core::array<SZipFileEntry> FileList;
core::string<c16> Base; core::string<c16> Base;
}; };
//! Archiveloader capable of loading MountPoint Archives //! Archiveloader capable of loading MountPoint Archives
class CArchiveLoaderMount : public IArchiveLoader class CArchiveLoaderMount : public IArchiveLoader
{ {
......
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