mirror of
https://github.com/minetest/irrlicht.git
synced 2025-07-01 15:50:27 +02:00
Merging r5975 through r6036 from trunk to ogl-es branch.
GLES drivers adapted, but only did make compile-tests. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@6038 dfc29bdd-3216-0410-991c-e03cc46cb475
This commit is contained in:
848
source/Irrlicht/CZipReader.cpp
Normal file
848
source/Irrlicht/CZipReader.cpp
Normal file
@ -0,0 +1,848 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CZipReader.h"
|
||||
|
||||
#include "os.h"
|
||||
|
||||
// This method is used for error output from bzip2.
|
||||
extern "C" void bz_internal_error(int errorCode)
|
||||
{
|
||||
irr::os::Printer::log("Error in bzip2 handling", irr::core::stringc(errorCode), irr::ELL_ERROR);
|
||||
}
|
||||
|
||||
#ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_
|
||||
|
||||
#include "CFileList.h"
|
||||
#include "CReadFile.h"
|
||||
#include "coreutil.h"
|
||||
|
||||
#include "IrrCompileConfig.h"
|
||||
#ifdef _IRR_COMPILE_WITH_ZLIB_
|
||||
#ifndef _IRR_USE_NON_SYSTEM_ZLIB_
|
||||
#include <zlib.h> // use system lib
|
||||
#else
|
||||
#include "zlib/zlib.h"
|
||||
#endif
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_
|
||||
#include "aesGladman/fileenc.h"
|
||||
#endif
|
||||
#ifdef _IRR_COMPILE_WITH_BZIP2_
|
||||
#ifndef _IRR_USE_NON_SYSTEM_BZLIB_
|
||||
#include <bzlib.h>
|
||||
#else
|
||||
#include "bzip2/bzlib.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _IRR_COMPILE_WITH_LZMA_
|
||||
#include "lzma/LzmaDec.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// zip loader
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
//! Constructor
|
||||
CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem* fs)
|
||||
: FileSystem(fs)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CArchiveLoaderZIP");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool CArchiveLoaderZIP::isALoadableFileFormat(const io::path& filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "zip", "pk3") ||
|
||||
core::hasFileExtension(filename, "gz", "tgz");
|
||||
}
|
||||
|
||||
//! Check to see if the loader can create archives of this type.
|
||||
bool CArchiveLoaderZIP::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
|
||||
{
|
||||
return (fileType == EFAT_ZIP || fileType == EFAT_GZIP);
|
||||
}
|
||||
|
||||
|
||||
//! Creates an archive from the filename
|
||||
/** \param file File handle to check.
|
||||
\return Pointer to newly created archive, or 0 upon error. */
|
||||
IFileArchive* CArchiveLoaderZIP::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
|
||||
{
|
||||
IFileArchive *archive = 0;
|
||||
io::IReadFile* file = FileSystem->createAndOpenFile(filename);
|
||||
|
||||
if (file)
|
||||
{
|
||||
archive = createArchive(file, ignoreCase, ignorePaths);
|
||||
file->drop();
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
//! creates/loads an archive from the file.
|
||||
//! \return Pointer to the created archive. Returns 0 if loading failed.
|
||||
IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
|
||||
{
|
||||
IFileArchive *archive = 0;
|
||||
if (file)
|
||||
{
|
||||
file->seek(0);
|
||||
|
||||
u16 sig;
|
||||
file->read(&sig, 2);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
sig = os::Byteswap::byteswap(sig);
|
||||
#endif
|
||||
|
||||
file->seek(0);
|
||||
|
||||
bool isGZip = (sig == 0x8b1f);
|
||||
|
||||
archive = new CZipReader(FileSystem, file, ignoreCase, ignorePaths, isGZip);
|
||||
}
|
||||
return archive;
|
||||
}
|
||||
|
||||
//! Check if the file might be loaded by this class
|
||||
/** Check might look into the file.
|
||||
\param file File handle to check.
|
||||
\return True if file seems to be loadable. */
|
||||
bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const
|
||||
{
|
||||
SZIPFileHeader header;
|
||||
|
||||
file->read( &header.Sig, 4 );
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.Sig = os::Byteswap::byteswap(header.Sig);
|
||||
#endif
|
||||
|
||||
return header.Sig == 0x04034b50 || // ZIP
|
||||
(header.Sig&0xffff) == 0x8b1f; // gzip
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// zip archive
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
CZipReader::CZipReader(IFileSystem* fs, IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip)
|
||||
: CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), FileSystem(fs), File(file), IsGZip(isGZip)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CZipReader");
|
||||
#endif
|
||||
|
||||
if (File)
|
||||
{
|
||||
File->grab();
|
||||
|
||||
// load file entries
|
||||
if (IsGZip)
|
||||
while (scanGZipHeader()) { }
|
||||
else
|
||||
while (scanZipHeader()) { }
|
||||
|
||||
sort();
|
||||
}
|
||||
}
|
||||
|
||||
CZipReader::~CZipReader()
|
||||
{
|
||||
if (File)
|
||||
File->drop();
|
||||
}
|
||||
|
||||
|
||||
//! get the archive type
|
||||
E_FILE_ARCHIVE_TYPE CZipReader::getType() const
|
||||
{
|
||||
return IsGZip ? EFAT_GZIP : EFAT_ZIP;
|
||||
}
|
||||
|
||||
const IFileList* CZipReader::getFileList() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
//! scans for a local header, returns false if there is no more local file header.
|
||||
//! 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.Offset = 0;
|
||||
memset(&entry.header, 0, sizeof(SZIPFileHeader));
|
||||
|
||||
// read header
|
||||
SGZIPMemberHeader header;
|
||||
if (File->read(&header, sizeof(SGZIPMemberHeader)) == sizeof(SGZIPMemberHeader))
|
||||
{
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.sig = os::Byteswap::byteswap(header.sig);
|
||||
header.time = 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__
|
||||
dataLen = os::Byteswap::byteswap(dataLen);
|
||||
#endif
|
||||
|
||||
// skip it
|
||||
File->seek(dataLen, true);
|
||||
}
|
||||
|
||||
io::path ZipFileName = "";
|
||||
|
||||
if (header.flags & EGZF_FILE_NAME)
|
||||
{
|
||||
c8 c;
|
||||
File->read(&c, 1);
|
||||
while (c)
|
||||
{
|
||||
ZipFileName.append(c);
|
||||
File->read(&c, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no file name?
|
||||
ZipFileName = Path;
|
||||
core::deletePathFromFilename(ZipFileName);
|
||||
|
||||
// rename tgz to tar or remove gz extension
|
||||
if (core::hasFileExtension(ZipFileName, "tgz"))
|
||||
{
|
||||
ZipFileName[ ZipFileName.size() - 2] = 'a';
|
||||
ZipFileName[ ZipFileName.size() - 1] = 'r';
|
||||
}
|
||||
else if (core::hasFileExtension(ZipFileName, "gz"))
|
||||
{
|
||||
ZipFileName[ ZipFileName.size() - 3] = 0;
|
||||
ZipFileName.validate();
|
||||
}
|
||||
}
|
||||
|
||||
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.Offset = File->getPos();
|
||||
|
||||
entry.header.FilenameLength = ZipFileName.size();
|
||||
|
||||
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__
|
||||
entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
|
||||
entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
|
||||
#endif
|
||||
|
||||
// now we've filled all the fields, this is just a standard deflate block
|
||||
addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, false, 0);
|
||||
FileInfo.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(bool ignoreGPBits)
|
||||
{
|
||||
io::path ZipFileName = "";
|
||||
SZipFileEntry entry;
|
||||
entry.Offset = 0;
|
||||
memset(&entry.header, 0, sizeof(SZIPFileHeader));
|
||||
|
||||
File->read(&entry.header, sizeof(SZIPFileHeader));
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
entry.header.Sig = os::Byteswap::byteswap(entry.header.Sig);
|
||||
entry.header.VersionToExtract = os::Byteswap::byteswap(entry.header.VersionToExtract);
|
||||
entry.header.GeneralBitFlag = os::Byteswap::byteswap(entry.header.GeneralBitFlag);
|
||||
entry.header.CompressionMethod = os::Byteswap::byteswap(entry.header.CompressionMethod);
|
||||
entry.header.LastModFileTime = os::Byteswap::byteswap(entry.header.LastModFileTime);
|
||||
entry.header.LastModFileDate = os::Byteswap::byteswap(entry.header.LastModFileDate);
|
||||
entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
|
||||
entry.header.DataDescriptor.CompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.CompressedSize);
|
||||
entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
|
||||
entry.header.FilenameLength = os::Byteswap::byteswap(entry.header.FilenameLength);
|
||||
entry.header.ExtraFieldLength = os::Byteswap::byteswap(entry.header.ExtraFieldLength);
|
||||
#endif
|
||||
|
||||
if (entry.header.Sig != 0x04034b50)
|
||||
return false; // local file headers end here.
|
||||
|
||||
// read filename
|
||||
{
|
||||
c8 *tmp = new c8 [ entry.header.FilenameLength + 2 ];
|
||||
File->read(tmp, entry.header.FilenameLength);
|
||||
tmp[entry.header.FilenameLength] = 0;
|
||||
ZipFileName = tmp;
|
||||
delete [] tmp;
|
||||
}
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_
|
||||
// AES encryption
|
||||
if ((entry.header.GeneralBitFlag & ZIP_FILE_ENCRYPTED) && (entry.header.CompressionMethod == 99))
|
||||
{
|
||||
s16 restSize = entry.header.ExtraFieldLength;
|
||||
SZipFileExtraHeader extraHeader;
|
||||
while (restSize)
|
||||
{
|
||||
File->read(&extraHeader, sizeof(extraHeader));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
extraHeader.ID = os::Byteswap::byteswap(extraHeader.ID);
|
||||
extraHeader.Size = os::Byteswap::byteswap(extraHeader.Size);
|
||||
#endif
|
||||
restSize -= sizeof(extraHeader);
|
||||
if (extraHeader.ID==(s16)0x9901)
|
||||
{
|
||||
SZipFileAESExtraData data;
|
||||
File->read(&data, sizeof(data));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
data.Version = os::Byteswap::byteswap(data.Version);
|
||||
data.CompressionMode = os::Byteswap::byteswap(data.CompressionMode);
|
||||
#endif
|
||||
restSize -= sizeof(data);
|
||||
if (data.Vendor[0]=='A' && data.Vendor[1]=='E')
|
||||
{
|
||||
// encode values into Sig
|
||||
// AE-Version | Strength | ActualMode
|
||||
entry.header.Sig =
|
||||
((data.Version & 0xff) << 24) |
|
||||
(data.EncryptionStrength << 16) |
|
||||
(data.CompressionMode);
|
||||
File->seek(restSize, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// move forward length of extra field.
|
||||
else
|
||||
#endif
|
||||
if (entry.header.ExtraFieldLength)
|
||||
File->seek(entry.header.ExtraFieldLength, true);
|
||||
|
||||
// if bit 3 was set, use CentralDirectory for setup
|
||||
if (!ignoreGPBits && entry.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRIPTOR)
|
||||
{
|
||||
SZIPFileCentralDirEnd dirEnd;
|
||||
FileInfo.clear();
|
||||
Files.clear();
|
||||
// First place where the end record could be stored
|
||||
File->seek(File->getSize()-22);
|
||||
const char endID[] = {0x50, 0x4b, 0x05, 0x06, 0x0};
|
||||
char tmp[5]={'\0'};
|
||||
bool found=false;
|
||||
// search for the end record ID
|
||||
while (!found && File->getPos()>0)
|
||||
{
|
||||
int seek=8;
|
||||
File->read(tmp, 4);
|
||||
switch (tmp[0])
|
||||
{
|
||||
case 0x50:
|
||||
if (!strcmp(endID, tmp))
|
||||
{
|
||||
seek=4;
|
||||
found=true;
|
||||
}
|
||||
break;
|
||||
case 0x4b:
|
||||
seek=5;
|
||||
break;
|
||||
case 0x05:
|
||||
seek=6;
|
||||
break;
|
||||
case 0x06:
|
||||
seek=7;
|
||||
break;
|
||||
}
|
||||
File->seek(-seek, true);
|
||||
}
|
||||
File->read(&dirEnd, sizeof(dirEnd));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
dirEnd.NumberDisk = os::Byteswap::byteswap(dirEnd.NumberDisk);
|
||||
dirEnd.NumberStart = os::Byteswap::byteswap(dirEnd.NumberStart);
|
||||
dirEnd.TotalDisk = os::Byteswap::byteswap(dirEnd.TotalDisk);
|
||||
dirEnd.TotalEntries = os::Byteswap::byteswap(dirEnd.TotalEntries);
|
||||
dirEnd.Size = os::Byteswap::byteswap(dirEnd.Size);
|
||||
dirEnd.Offset = os::Byteswap::byteswap(dirEnd.Offset);
|
||||
dirEnd.CommentLength = os::Byteswap::byteswap(dirEnd.CommentLength);
|
||||
#endif
|
||||
FileInfo.reallocate(dirEnd.TotalEntries);
|
||||
File->seek(dirEnd.Offset);
|
||||
while (scanCentralDirectoryHeader()) { }
|
||||
return false;
|
||||
}
|
||||
|
||||
// store position in file
|
||||
entry.Offset = File->getPos();
|
||||
// move forward length of data
|
||||
File->seek(entry.header.DataDescriptor.CompressedSize, true);
|
||||
|
||||
#ifdef _DEBUG
|
||||
//os::Debuginfo::print("added file from archive", ZipFileName.c_str());
|
||||
#endif
|
||||
|
||||
addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, ZipFileName.lastChar()=='/', FileInfo.size());
|
||||
FileInfo.push_back(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//! scans for a local header, returns false if there is no more local file header.
|
||||
bool CZipReader::scanCentralDirectoryHeader()
|
||||
{
|
||||
io::path ZipFileName = "";
|
||||
SZIPFileCentralDirFileHeader entry;
|
||||
File->read(&entry, sizeof(SZIPFileCentralDirFileHeader));
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
entry.Sig = os::Byteswap::byteswap(entry.Sig);
|
||||
entry.VersionMadeBy = os::Byteswap::byteswap(entry.VersionMadeBy);
|
||||
entry.VersionToExtract = os::Byteswap::byteswap(entry.VersionToExtract);
|
||||
entry.GeneralBitFlag = os::Byteswap::byteswap(entry.GeneralBitFlag);
|
||||
entry.CompressionMethod = os::Byteswap::byteswap(entry.CompressionMethod);
|
||||
entry.LastModFileTime = os::Byteswap::byteswap(entry.LastModFileTime);
|
||||
entry.LastModFileDate = os::Byteswap::byteswap(entry.LastModFileDate);
|
||||
entry.CRC32 = os::Byteswap::byteswap(entry.CRC32);
|
||||
entry.CompressedSize = os::Byteswap::byteswap(entry.CompressedSize);
|
||||
entry.UncompressedSize = os::Byteswap::byteswap(entry.UncompressedSize);
|
||||
entry.FilenameLength = os::Byteswap::byteswap(entry.FilenameLength);
|
||||
entry.ExtraFieldLength = os::Byteswap::byteswap(entry.ExtraFieldLength);
|
||||
entry.FileCommentLength = os::Byteswap::byteswap(entry.FileCommentLength);
|
||||
entry.DiskNumberStart = os::Byteswap::byteswap(entry.DiskNumberStart);
|
||||
entry.InternalFileAttributes = os::Byteswap::byteswap(entry.InternalFileAttributes);
|
||||
entry.ExternalFileAttributes = os::Byteswap::byteswap(entry.ExternalFileAttributes);
|
||||
entry.RelativeOffsetOfLocalHeader = os::Byteswap::byteswap(entry.RelativeOffsetOfLocalHeader);
|
||||
#endif
|
||||
|
||||
if (entry.Sig != 0x02014b50)
|
||||
return false; // central dir headers end here.
|
||||
|
||||
const long pos = File->getPos();
|
||||
File->seek(entry.RelativeOffsetOfLocalHeader);
|
||||
scanZipHeader(true);
|
||||
File->seek(pos+entry.FilenameLength+entry.ExtraFieldLength+entry.FileCommentLength);
|
||||
FileInfo.getLast().header.DataDescriptor.CompressedSize=entry.CompressedSize;
|
||||
FileInfo.getLast().header.DataDescriptor.UncompressedSize=entry.UncompressedSize;
|
||||
FileInfo.getLast().header.DataDescriptor.CRC32=entry.CRC32;
|
||||
Files.getLast().Size=entry.UncompressedSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//! opens a file by file name
|
||||
IReadFile* CZipReader::createAndOpenFile(const io::path& filename)
|
||||
{
|
||||
s32 index = findFile(filename, false);
|
||||
|
||||
if (index != -1)
|
||||
return createAndOpenFile(index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_LZMA_
|
||||
//! Used for LZMA decompression. The lib has no default memory management
|
||||
namespace
|
||||
{
|
||||
void *SzAlloc(void *p, size_t size)
|
||||
{
|
||||
(void)p; // disable unused variable warnings
|
||||
return malloc(size);
|
||||
}
|
||||
void SzFree(void *p, void *address)
|
||||
{
|
||||
(void)p; // disable unused variable warnings
|
||||
free(address);
|
||||
}
|
||||
ISzAlloc lzmaAlloc = { SzAlloc, SzFree };
|
||||
}
|
||||
#endif
|
||||
|
||||
//! opens a file by index
|
||||
IReadFile* CZipReader::createAndOpenFile(u32 index)
|
||||
{
|
||||
// Irrlicht supports 0, 8, 12, 14, 99
|
||||
//0 - The file is stored (no compression)
|
||||
//1 - The file is Shrunk
|
||||
//2 - The file is Reduced with compression factor 1
|
||||
//3 - The file is Reduced with compression factor 2
|
||||
//4 - The file is Reduced with compression factor 3
|
||||
//5 - The file is Reduced with compression factor 4
|
||||
//6 - The file is Imploded
|
||||
//7 - Reserved for Tokenizing compression algorithm
|
||||
//8 - The file is Deflated
|
||||
//9 - Reserved for enhanced Deflating
|
||||
//10 - PKWARE Date Compression Library Imploding
|
||||
//12 - bzip2 - Compression Method from libbz2, WinZip 10
|
||||
//14 - LZMA - Compression Method, WinZip 12
|
||||
//96 - Jpeg compression - Compression Method, WinZip 12
|
||||
//97 - WavPack - Compression Method, WinZip 11
|
||||
//98 - PPMd - Compression Method, WinZip 10
|
||||
//99 - AES encryption, WinZip 9
|
||||
|
||||
const SZipFileEntry &e = FileInfo[Files[index].ID];
|
||||
wchar_t buf[64];
|
||||
s16 actualCompressionMethod=e.header.CompressionMethod;
|
||||
IReadFile* decrypted=0;
|
||||
u8* decryptedBuf=0;
|
||||
u32 decryptedSize=e.header.DataDescriptor.CompressedSize;
|
||||
#ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_
|
||||
if ((e.header.GeneralBitFlag & ZIP_FILE_ENCRYPTED) && (e.header.CompressionMethod == 99))
|
||||
{
|
||||
os::Printer::log("Reading encrypted file.");
|
||||
u8 salt[16]={0};
|
||||
const u16 saltSize = (((e.header.Sig & 0x00ff0000) >>16)+1)*4;
|
||||
File->seek(e.Offset);
|
||||
File->read(salt, saltSize);
|
||||
char pwVerification[2];
|
||||
char pwVerificationFile[2];
|
||||
File->read(pwVerification, 2);
|
||||
fcrypt_ctx zctx; // the encryption context
|
||||
int rc = fcrypt_init(
|
||||
(e.header.Sig & 0x00ff0000) >>16,
|
||||
(const unsigned char*)Password.c_str(), // the password
|
||||
Password.size(), // number of bytes in password
|
||||
salt, // the salt
|
||||
(unsigned char*)pwVerificationFile, // on return contains password verifier
|
||||
&zctx); // encryption context
|
||||
if (strncmp(pwVerificationFile, pwVerification, 2))
|
||||
{
|
||||
os::Printer::log("Wrong password");
|
||||
return 0;
|
||||
}
|
||||
decryptedSize= e.header.DataDescriptor.CompressedSize-saltSize-12;
|
||||
decryptedBuf= new u8[decryptedSize];
|
||||
u32 c = 0;
|
||||
while ((c+32768)<=decryptedSize)
|
||||
{
|
||||
File->read(decryptedBuf+c, 32768);
|
||||
fcrypt_decrypt(
|
||||
decryptedBuf+c, // pointer to the data to decrypt
|
||||
32768, // how many bytes to decrypt
|
||||
&zctx); // decryption context
|
||||
c+=32768;
|
||||
}
|
||||
File->read(decryptedBuf+c, decryptedSize-c);
|
||||
fcrypt_decrypt(
|
||||
decryptedBuf+c, // pointer to the data to decrypt
|
||||
decryptedSize-c, // how many bytes to decrypt
|
||||
&zctx); // decryption context
|
||||
|
||||
char fileMAC[10];
|
||||
char resMAC[10];
|
||||
rc = fcrypt_end(
|
||||
(unsigned char*)resMAC, // on return contains the authentication code
|
||||
&zctx); // encryption context
|
||||
if (rc != 10)
|
||||
{
|
||||
os::Printer::log("Error on encryption closing");
|
||||
delete [] decryptedBuf;
|
||||
return 0;
|
||||
}
|
||||
File->read(fileMAC, 10);
|
||||
if (strncmp(fileMAC, resMAC, 10))
|
||||
{
|
||||
os::Printer::log("Error on encryption check");
|
||||
delete [] decryptedBuf;
|
||||
return 0;
|
||||
}
|
||||
decrypted = FileSystem->createMemoryReadFile(decryptedBuf, decryptedSize, Files[index].FullName, true);
|
||||
actualCompressionMethod = (e.header.Sig & 0xffff);
|
||||
#if 0
|
||||
if ((e.header.Sig & 0xff000000)==0x01000000)
|
||||
{
|
||||
}
|
||||
else if ((e.header.Sig & 0xff000000)==0x02000000)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
os::Printer::log("Unknown encryption method");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
switch(actualCompressionMethod)
|
||||
{
|
||||
case 0: // no compression
|
||||
{
|
||||
if (decrypted)
|
||||
return decrypted;
|
||||
else
|
||||
return createLimitReadFile(Files[index].FullName, File, e.Offset, decryptedSize);
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_ZLIB_
|
||||
|
||||
const u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize;
|
||||
c8* pBuf = new c8[ uncompressedSize ];
|
||||
if (!pBuf)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *pcData = decryptedBuf;
|
||||
if (!pcData)
|
||||
{
|
||||
pcData = new u8[decryptedSize];
|
||||
if (!pcData)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//memset(pcData, 0, decryptedSize);
|
||||
File->seek(e.Offset);
|
||||
File->read(pcData, decryptedSize);
|
||||
}
|
||||
|
||||
// Setup the inflate stream.
|
||||
z_stream stream;
|
||||
s32 err;
|
||||
|
||||
stream.next_in = (Bytef*)pcData;
|
||||
stream.avail_in = (uInt)decryptedSize;
|
||||
stream.next_out = (Bytef*)pBuf;
|
||||
stream.avail_out = uncompressedSize;
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
|
||||
// Perform inflation. wbits < 0 indicates no zlib header inside the data.
|
||||
err = inflateInit2(&stream, -MAX_WBITS);
|
||||
if (err == Z_OK)
|
||||
{
|
||||
err = inflate(&stream, Z_FINISH);
|
||||
inflateEnd(&stream);
|
||||
if (err == Z_STREAM_END)
|
||||
err = Z_OK;
|
||||
err = Z_OK;
|
||||
inflateEnd(&stream);
|
||||
}
|
||||
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
else
|
||||
delete[] pcData;
|
||||
|
||||
if (err != Z_OK)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Error decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return FileSystem->createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true);
|
||||
|
||||
#else
|
||||
return 0; // zlib not compiled, we cannot decompress the data.
|
||||
#endif
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_BZIP2_
|
||||
|
||||
const u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize;
|
||||
c8* pBuf = new c8[ uncompressedSize ];
|
||||
if (!pBuf)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *pcData = decryptedBuf;
|
||||
if (!pcData)
|
||||
{
|
||||
pcData = new u8[decryptedSize];
|
||||
if (!pcData)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//memset(pcData, 0, decryptedSize);
|
||||
File->seek(e.Offset);
|
||||
File->read(pcData, decryptedSize);
|
||||
}
|
||||
|
||||
bz_stream bz_ctx;
|
||||
memset(&bz_ctx, 0, sizeof(bz_ctx));
|
||||
/* use BZIP2's default memory allocation
|
||||
bz_ctx->bzalloc = NULL;
|
||||
bz_ctx->bzfree = NULL;
|
||||
bz_ctx->opaque = NULL;
|
||||
*/
|
||||
int err = BZ2_bzDecompressInit(&bz_ctx, 0, 0); /* decompression */
|
||||
if(err != BZ_OK)
|
||||
{
|
||||
os::Printer::log("bzip2 decompression failed. File cannot be read.", ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
bz_ctx.next_in = (char*)pcData;
|
||||
bz_ctx.avail_in = decryptedSize;
|
||||
/* pass all input to decompressor */
|
||||
bz_ctx.next_out = pBuf;
|
||||
bz_ctx.avail_out = uncompressedSize;
|
||||
err = BZ2_bzDecompress(&bz_ctx);
|
||||
err = BZ2_bzDecompressEnd(&bz_ctx);
|
||||
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
else
|
||||
delete[] pcData;
|
||||
|
||||
if (err != BZ_OK)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Error decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return FileSystem->createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true);
|
||||
|
||||
#else
|
||||
os::Printer::log("bzip2 decompression not supported. File cannot be read.", ELL_ERROR);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
case 14:
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_LZMA_
|
||||
|
||||
u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize;
|
||||
c8* pBuf = new c8[ uncompressedSize ];
|
||||
if (!pBuf)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *pcData = decryptedBuf;
|
||||
if (!pcData)
|
||||
{
|
||||
pcData = new u8[decryptedSize];
|
||||
if (!pcData)
|
||||
{
|
||||
swprintf_irr ( buf, 64, L"Not enough memory for decompressing %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//memset(pcData, 0, decryptedSize);
|
||||
File->seek(e.Offset);
|
||||
File->read(pcData, decryptedSize);
|
||||
}
|
||||
|
||||
ELzmaStatus status;
|
||||
SizeT tmpDstSize = uncompressedSize;
|
||||
SizeT tmpSrcSize = decryptedSize;
|
||||
|
||||
unsigned int propSize = (pcData[3]<<8)+pcData[2];
|
||||
int err = LzmaDecode((Byte*)pBuf, &tmpDstSize,
|
||||
pcData+4+propSize, &tmpSrcSize,
|
||||
pcData+4, propSize,
|
||||
e.header.GeneralBitFlag&0x1?LZMA_FINISH_END:LZMA_FINISH_ANY, &status,
|
||||
&lzmaAlloc);
|
||||
uncompressedSize = tmpDstSize; // may be different to expected value
|
||||
|
||||
if (decrypted)
|
||||
decrypted->drop();
|
||||
else
|
||||
delete[] pcData;
|
||||
|
||||
if (err != SZ_OK)
|
||||
{
|
||||
os::Printer::log( "Error decompressing", Files[index].FullName, ELL_ERROR);
|
||||
delete [] pBuf;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return FileSystem->createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true);
|
||||
|
||||
#else
|
||||
os::Printer::log("lzma decompression not supported. File cannot be read.", ELL_ERROR);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
case 99:
|
||||
// If we come here with an encrypted file, decryption support is missing
|
||||
os::Printer::log("Decryption support not enabled. File cannot be read.", ELL_ERROR);
|
||||
return 0;
|
||||
default:
|
||||
swprintf_irr ( buf, 64, L"file has unsupported compression method. %s", core::stringw(Files[index].FullName).c_str() );
|
||||
os::Printer::log( buf, ELL_ERROR);
|
||||
return 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
|
||||
#endif // __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_
|
Reference in New Issue
Block a user