tge/engine/core/zipAggregate.cc
2017-04-17 06:17:10 -06:00

243 lines
7.2 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, 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)
{
// First figure out whether we are looking at a directory
// or a file. Directories have a trailing / in the file name
// and a filelength of 0
if (in_rHeader.m_pFileName[dStrlen(in_rHeader.m_pFileName) - 1] == '/' &&
(in_rHeader.m_header.compressedSize == 0 &&
in_rHeader.m_header.uncompressedSize == 0))
return;
// We have a file if we are here, so enter it
// into the directory
m_fileList.increment();
FileEntry& rEntry = m_fileList.last();
// Copy the path to a file within a zip to tempString
char tempString[1024];
dStrcpy(tempString, in_rHeader.m_pFileName);
// Iterate through the string and change any
// characters with \\ to /
char* scan = tempString;
while (*scan != '\0')
{
if (*scan == '\\')
*scan = '/';
scan++;
}
// Allocate memory for zipPath and then copy the
// full path of the zip, so thats
// <location>/<name>.zip
char* zipPath = new char[dStrlen(m_pZipFileName) + dStrlen(tempString) + 2];
dStrcpy(zipPath, m_pZipFileName);
// Get all the text from the . until the end of
// the string
char* dot = dStrrchr(zipPath, '.') - 2;
// Kill the extension so the zipPath string
// becomes <location>/<name>
dot[2] = '\0';
// Create the FULL path to the contents within a zip now
// So zipPath becomes
// <harddrive path>/<zip name>/<zip path>/<zip files>.*
dStrcat(zipPath, "/");
dStrcat(zipPath, tempString);
// Create file base name
char* pPathEnd = dStrrchr(zipPath, '/');
if(pPathEnd != NULL)
{
// Put our path and file into the string table and store
// them for the ResourceManager
pPathEnd[0] = '\0';
rEntry.pPath = StringTable->insert(zipPath);
rEntry.pFileName = StringTable->insert(pPathEnd + 1);
}
// Tell ResourceManger the appropriate file attributes
rEntry.fileSize = in_rHeader.m_header.uncompressedSize;
rEntry.compressedFileSize = in_rHeader.m_header.compressedSize;
rEntry.fileOffset = in_rHeader.m_header.relativeOffsetOfLocalHeader;
// Tell ResourceManager the appropriate file compressions used on the file
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
{
// We can't have anything other than Stored or Deflated zips
AssertWarn(0, avar("Warning, non-stored or deflated resource in %s", m_pZipFileName));
m_fileList.decrement();
}
// important!! or we have memory leaks :)
delete [] zipPath;
}