engine/core/zipAggregate.cc
2024-01-07 04:36:33 +00:00

213 lines
6.2 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "Platform/platform.h"
#include "Core/stringTable.h"
#include "Core/fileStream.h" // Streams
#include "Core/zipAggregate.h" // Own header, and private includes
#include "Core/zipHeaders.h"
ZipAggregate::ZipAggregate()
: m_pZipFileName(NULL)
{
VECTOR_SET_ASSOCIATION(m_fileList);
}
ZipAggregate::~ZipAggregate()
{
closeAggregate();
}
bool
ZipAggregate::refreshAggregate()
{
AssertFatal(m_pZipFileName != NULL, "No filename? Must not be open. Disallowed");
char tmpBuff[512];
dStrcpy(tmpBuff, m_pZipFileName);
return openAggregate(tmpBuff);
}
bool
ZipAggregate::openAggregate(const char* in_pFileName)
{
closeAggregate();
AssertFatal(in_pFileName != NULL, "No filename to open!");
m_pZipFileName = new char[dStrlen(in_pFileName) + 1];
dStrcpy(m_pZipFileName, in_pFileName);
FileStream* pStream = new FileStream;
if (pStream->open(m_pZipFileName, FileStream::Read) == false ||
createZipDirectory(pStream) == false) {
// Failure, abort the open...
//
delete pStream;
delete [] m_pZipFileName;
m_pZipFileName = NULL;
return false;
}
// Finished! Open for business
delete pStream;
return true;
}
void
ZipAggregate::closeAggregate()
{
destroyZipDirectory();
delete [] m_pZipFileName;
m_pZipFileName = NULL;
}
void
ZipAggregate::destroyZipDirectory()
{
m_fileList.clear();
}
bool
ZipAggregate::createZipDirectory(Stream* io_pStream)
{
AssertFatal(io_pStream != NULL, "Error, stream not open.");
U32 streamSize = io_pStream->getStreamSize();
U32 initialPosition = io_pStream->getPosition();
// We assume that the CD is 22 bytes from the end. This will be invalid
// in the case that the zip file has comments. Perhaps test the quick
// way, then degrade to seaching the final 64k+22b (!) of the stream?
//
bool posSuccess = io_pStream->setPosition(streamSize - sizeof(ZipEOCDRecord::EOCDRecord));
if (posSuccess == false) {
AssertWarn(false, "Unable to position stream to start of EOCDRecord");
return false;
}
ZipEOCDRecord* pEOCDRecord = new ZipEOCDRecord;
if (pEOCDRecord->readFromStream(*io_pStream) == false) {
// This is where we would try to degrade to general case...
//
AssertWarn(false, "Unable to locate central directory. "
"Zip File might have comments");
delete pEOCDRecord;
return false;
}
// Check the consistency of the zipFile.
//
if ((pEOCDRecord->m_record.diskNumber != pEOCDRecord->m_record.eocdDiskNumber) ||
(pEOCDRecord->m_record.numCDEntriesDisk != pEOCDRecord->m_record.numCDEntriesTotal)) {
AssertWarn(false, "Zipfile appears to be part of a "
"multi-zip disk span set, unsupported");
delete pEOCDRecord;
return false;
}
// If we're here, we're good! Scan to the start of the CDirectory, and
// start scanning the entries into our directory structure...
//
U32 startCDPosition = pEOCDRecord->m_record.cdOffset;
U32 endCDPosition = pEOCDRecord->m_record.cdOffset +
pEOCDRecord->m_record.cdSize;
posSuccess = io_pStream->setPosition(startCDPosition);
if (posSuccess == false) {
AssertWarn(false, "Unable to position to CD entries.");
delete pEOCDRecord;
return false;
}
bool dirReadSuccess = true;
for (U16 i = 0; i < pEOCDRecord->m_record.numCDEntriesTotal; i++) {
ZipDirFileHeader zdfHeader;
bool hrSuccess = zdfHeader.readFromStream(*io_pStream);
if (hrSuccess == false) {
AssertWarn(false, "Error reading a CD Entry in zip aggregate");
dirReadSuccess = false;
break;
}
enterZipDirRecord(zdfHeader);
}
delete pEOCDRecord;
if (dirReadSuccess == true) {
// Every thing went well, we're done, position the stream to the end of the
// CD...
//
io_pStream->setPosition(endCDPosition);
return true;
} else {
// Oh, crap.
io_pStream->setPosition(initialPosition);
destroyZipDirectory();
return false;
}
}
void
ZipAggregate::enterZipDirRecord(const ZipDirFileHeader& in_rHeader)
{
// Ok, the first thing to do is figure out whether this is
// a directory or a file. Directories have a trailing /
// in the file name, and a filelength (comp/uncomp) of 0
// Note: this is not specified in
// the file format spec I have, but seems fairly likely to
// be correct.
//
if (in_rHeader.m_pFileName[dStrlen(in_rHeader.m_pFileName) - 1] == '/' &&
(in_rHeader.m_header.compressedSize == 0 &&
in_rHeader.m_header.uncompressedSize == 0))
return;
// It's a file. Enter it into the directory...
m_fileList.increment();
FileEntry& rEntry = m_fileList.last();
char tempString[1024];
dStrcpy(tempString, in_rHeader.m_pFileName);
char* scan = tempString;
while (*scan != '\0') {
if (*scan == '\\')
*scan = '/';
scan++;
}
char* pPathEnd = dStrrchr(tempString, '/');
if (pPathEnd != NULL) {
pPathEnd[0] = '\0';
rEntry.pPath = StringTable->insert(tempString);
rEntry.pFileName = StringTable->insert(pPathEnd + 1);
} else {
rEntry.pPath = NULL;
rEntry.pFileName = StringTable->insert(in_rHeader.m_pFileName);
}
rEntry.fileSize = in_rHeader.m_header.uncompressedSize;
rEntry.compressedFileSize = in_rHeader.m_header.compressedSize;
rEntry.fileOffset = in_rHeader.m_header.relativeOffsetOfLocalHeader;
if (in_rHeader.m_header.compressionMethod == ZipDirFileHeader::Deflated) {
rEntry.flags = FileEntry::Compressed;
} else if (in_rHeader.m_header.compressionMethod == ZipDirFileHeader::Stored) {
rEntry.flags = FileEntry::Uncompressed;
} else {
AssertWarn(0, avar("Warning, non-stored or deflated resource in %s",
m_pZipFileName));
m_fileList.decrement();
}
}