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-e03cc46cb475master
parent
1c9169372a
commit
a7f1c19c44
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue