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
master
bitplane 2009-07-07 10:45:48 +00:00
parent 1c9169372a
commit a7f1c19c44
5 changed files with 194 additions and 75 deletions

View File

@ -2,8 +2,6 @@ Changes in 1.6
- IFileSystem changes:
- Added TAR archive loader.
- Renamed the following functions-
IFileArchive::getArchiveType to getType
IFileSystem::registerFileArchive to addFileArchive
@ -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.
- 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.
- IOSOperator::getTextFromClipboard returns now const c8* instead of c8*

View File

@ -23,16 +23,16 @@ enum EFileSystemType
//! Contains the different types of archives
enum E_FILE_ARCHIVE_TYPE
{
//! A ZIP archive
//! A PKZIP or gzip archive
EFAT_ZIP = MAKE_IRR_ID('Z','I','P', 0),
//! A virtual directory
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),
//! A Tape ARchive file
//! A Tape ARchive
EFAT_TAR = MAKE_IRR_ID('T','A','R', 0),
//! The type of this archive is unknown

View File

@ -85,11 +85,6 @@ bool CArchiveLoaderTAR::isALoadableFileFormat(io::IReadFile* file) const
STarHeader fHead;
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;
sscanf(fHead.Checksum, "%lo", &checksum);
@ -167,11 +162,6 @@ u32 CTarReader::populateFileList()
// read the header
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
if (fHead.Link == ETLI_REGULAR_FILE || ETLI_REGULAR_FILE_OLD)
{

View File

@ -22,8 +22,13 @@ namespace irr
namespace io
{
// -----------------------------------------------------------------------------
// zip loader
// -----------------------------------------------------------------------------
//! Constructor
CArchiveLoaderZIP::CArchiveLoaderZIP( io::IFileSystem* fs)
CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem* fs)
: FileSystem(fs)
{
#ifdef _DEBUG
@ -31,17 +36,10 @@ CArchiveLoaderZIP::CArchiveLoaderZIP( io::IFileSystem* fs)
#endif
}
//! destructor
CArchiveLoaderZIP::~CArchiveLoaderZIP()
{
}
//! returns true if the file maybe is able to be loaded by this class
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
if (file)
{
archive = createArchive ( file, ignoreCase, ignorePaths );
file->drop ();
archive = createArchive(file, ignoreCase, ignorePaths);
file->drop();
}
return archive;
@ -67,10 +65,22 @@ IFileArchive* CArchiveLoaderZIP::createArchive(const core::string<c16>& filename
IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
{
IFileArchive *archive = 0;
if ( file )
if (file)
{
file->seek ( 0 );
archive = new CZipReader(file, ignoreCase, ignorePaths);
file->seek(0);
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;
}
@ -87,14 +97,17 @@ bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(header.Sig);
#endif
return header.Sig == 0x04034b50;
return header.Sig == 0x04034b50 || // ZIP
*((u16*)(&header.Sig)) == 0x8b1f; // gzip
}
/*
ZIP Archive
*/
CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
: File(file), IgnoreCase(ignoreCase), IgnorePaths(ignorePaths)
// -----------------------------------------------------------------------------
// zip archive
// -----------------------------------------------------------------------------
CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip)
: File(file), IgnoreCase(ignoreCase), IgnorePaths(ignorePaths), IsGZip(isGZip)
{
#ifdef _DEBUG
setDebugName("CZipReader");
@ -107,9 +120,11 @@ CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
Base = File->getFileName();
Base.replace ( '\\', '/' );
// scan local headers
while (scanLocalHeader()) ;
//while (scanLocalHeader2());
// load file entries
if (IsGZip)
while (scanGZipHeader()) { }
else
while (scanZipHeader()) { }
// prepare file index for binary search
FileList.sort();
@ -122,8 +137,6 @@ CZipReader::~CZipReader()
File->drop();
}
//! splits filename from zip file into useful filenames and paths
void CZipReader::extractFilename(SZipFileEntry* entry)
{
@ -219,7 +232,7 @@ bool CZipReader::scanLocalHeader2()
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
File->seek(sizeof(SZIPFileDataDescriptor), true );
@ -258,7 +271,98 @@ bool CZipReader::scanLocalHeader2()
#endif
//! 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];
@ -302,7 +406,7 @@ bool CZipReader::scanLocalHeader()
File->seek(entry.header.ExtraFieldLength, true);
// 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
File->read(&entry.header.DataDescriptor, sizeof(entry.header.DataDescriptor));
@ -361,7 +465,7 @@ IReadFile* CZipReader::createAndOpenFile(u32 index)
{
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:
{
@ -413,7 +517,6 @@ IReadFile* CZipReader::createAndOpenFile(u32 index)
inflateEnd(&stream);
}
delete[] pcData;
if (err != Z_OK)
@ -453,7 +556,7 @@ const IFileArchiveEntry* CZipReader::getFileInfo(u32 index)
//! return the id of the file Archive
const core::string<c16>& CZipReader::getArchiveName ()
const core::string<c16>& CZipReader::getArchiveName()
{
return Base;
}
@ -488,7 +591,8 @@ s32 CZipReader::findFile( const core::string<c16>& simpleFilename)
return res;
}
// -----------------------------------------------------------------------------
// mount archive loader
// -----------------------------------------------------------------------------
//! Constructor
@ -511,10 +615,10 @@ CArchiveLoaderMount::~CArchiveLoaderMount()
bool CArchiveLoaderMount::isALoadableFileFormat(const core::string<c16>& filename) const
{
bool ret = false;
core::string<c16> fname ( filename );
deletePathFromFilename ( fname );
core::string<c16> fname(filename);
deletePathFromFilename(fname);
if ( 0 == fname.size() )
if (!fname.size())
{
ret = true;
}
@ -528,19 +632,19 @@ IFileArchive* CArchiveLoaderMount::createArchive(const core::string<c16>& filena
{
IFileArchive *archive = 0;
EFileSystemType current = FileSystem->setFileListSystem ( FILESYSTEM_NATIVE );
EFileSystemType current = FileSystem->setFileListSystem(FILESYSTEM_NATIVE);
core::string<c16> save = FileSystem->getWorkingDirectory ();
core::string<c16> fullPath = FileSystem->getAbsolutePath ( filename );
FileSystem->flattenFilename ( fullPath );
core::string<c16> save = FileSystem->getWorkingDirectory();
core::string<c16> fullPath = FileSystem->getAbsolutePath(filename);
FileSystem->flattenFilename(fullPath);
if ( FileSystem->changeWorkingDirectoryTo ( fullPath ) )
{
archive = new CMountPointReader(FileSystem, fullPath, ignoreCase, ignorePaths);
}
FileSystem->changeWorkingDirectoryTo ( save );
FileSystem->setFileListSystem ( current );
FileSystem->changeWorkingDirectoryTo(save);
FileSystem->setFileListSystem(current);
return archive;
}
@ -563,6 +667,10 @@ IFileArchive* CArchiveLoaderMount::createArchive(io::IReadFile* file, bool ignor
#if 1
// -----------------------------------------------------------------------------
// mount point reader
// -----------------------------------------------------------------------------
//! simple Reader ( does not handle ignorecase and ignorePath )
// its more a simple wrapper for handling relative directories
// advantage: speed
@ -595,7 +703,7 @@ CMountPointReader::CMountPointReader( IFileSystem * parent, const core::string<c
Base.append ( '/' );
}
void CMountPointReader::buildDirectory ( )
void CMountPointReader::buildDirectory()
{
}

View File

@ -20,7 +20,7 @@ namespace io
const s16 ZIP_FILE_ENCRYPTED = 0x0001;
// the fields crc-32, compressed size and uncompressed size are set to
// 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__)
# pragma pack( push, packing )
@ -94,6 +94,25 @@ namespace io
// zipfile comment (variable size)
} 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
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop, packing )
@ -120,9 +139,6 @@ namespace io
//! Constructor
CArchiveLoaderZIP(io::IFileSystem* fs);
//! destructor
virtual ~CArchiveLoaderZIP();
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".zip")
virtual bool isALoadableFileFormat(const core::string<c16>& filename) const;
@ -151,14 +167,15 @@ namespace io
/*!
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
{
public:
CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths);
//! constructor
CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip=false);
//! destructor
virtual ~CZipReader();
//! opens a file by file name
@ -177,33 +194,32 @@ namespace io
virtual s32 findFile(const core::string<c16>& filename);
//! return the id of the file Archive
virtual const core::string<c16>& getArchiveName ();
virtual const core::string<c16>& getArchiveName();
//! get the class Type
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:
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
void extractFilename(SZipFileEntry* entry);
bool IgnoreCase;
bool IgnorePaths;
bool IsGZip;
core::array<SZipFileEntry> FileList;
core::string<c16> Base;
};
//! Archiveloader capable of loading MountPoint Archives
class CArchiveLoaderMount : public IArchiveLoader
{