From ab8d13ee5806f7d9cf6e352482934ad371a55e11 Mon Sep 17 00:00:00 2001 From: Einar Johan Trøan Sømåen Date: Wed, 25 Jul 2012 03:20:17 +0200 Subject: WINTERMUTE: Change the DCP-file access to work as an Common::Archive. --- engines/wintermute/base/base_file_manager.cpp | 236 ++-------------------- engines/wintermute/base/base_file_manager.h | 12 +- engines/wintermute/base/file/BPkgFile.cpp | 107 ---------- engines/wintermute/base/file/BPkgFile.h | 49 ----- engines/wintermute/base/file/base_file_entry.cpp | 53 +++++ engines/wintermute/base/file/base_file_entry.h | 6 +- engines/wintermute/base/file/base_package.cpp | 245 ++++++++++++++++++----- engines/wintermute/base/file/base_package.h | 58 ++++-- engines/wintermute/module.mk | 1 - 9 files changed, 320 insertions(+), 447 deletions(-) delete mode 100644 engines/wintermute/base/file/BPkgFile.cpp delete mode 100644 engines/wintermute/base/file/BPkgFile.h diff --git a/engines/wintermute/base/base_file_manager.cpp b/engines/wintermute/base/base_file_manager.cpp index 3317b01c6f..2661081528 100644 --- a/engines/wintermute/base/base_file_manager.cpp +++ b/engines/wintermute/base/base_file_manager.cpp @@ -29,13 +29,10 @@ #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/file/base_disk_file.h" #include "engines/wintermute/base/file/base_save_thumb_file.h" -#include "engines/wintermute/base/file/base_file_entry.h" #include "engines/wintermute/base/file/base_package.h" #include "engines/wintermute/base/file/base_resources.h" -#include "engines/wintermute/base/file/BPkgFile.h" #include "engines/wintermute/base/base_registry.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/file/dcpackage.h" #include "engines/wintermute/wintermute.h" #include "common/str.h" #include "common/tokenizer.h" @@ -71,31 +68,18 @@ bool BaseFileManager::cleanup() { // delete registered paths _packagePaths.clear(); - // delete file entries - _filesIter = _files.begin(); - while (_filesIter != _files.end()) { - delete _filesIter->_value; - _filesIter++; - } - _files.clear(); - // close open files for (uint32 i = 0; i < _openFiles.size(); i++) { delete _openFiles[i]; } _openFiles.clear(); - // delete packages - for (uint32 i = 0; i < _packages.size(); i++) - delete _packages[i]; _packages.clear(); return STATUS_OK; } - - ////////////////////////////////////////////////////////////////////// byte *BaseFileManager::readWholeFile(const Common::String &filename, uint32 *size, bool mustExist) { byte *buffer = NULL; @@ -149,19 +133,6 @@ bool BaseFileManager::saveFile(const Common::String &filename, byte *buffer, uin return STATUS_OK; } - -////////////////////////////////////////////////////////////////////////// -bool BaseFileManager::requestCD(int cd, char *packageFile, const char *filename) { - // unmount all non-local packages - for (uint32 i = 0; i < _packages.size(); i++) { - if (_packages[i]->_cd > 0) _packages[i]->close(); - } - - - return STATUS_FAILED; -} - - ////////////////////////////////////////////////////////////////////////// bool BaseFileManager::addPath(TPathType type, const Common::FSNode &path) { if (!path.exists()) @@ -189,8 +160,6 @@ bool BaseFileManager::reloadPaths() { return initPaths(); } - -#define TEMP_BUFFER_SIZE 32768 ////////////////////////////////////////////////////////////////////////// bool BaseFileManager::initPaths() { if (!_gameRef) // This function only works when the game-registry is loaded @@ -248,7 +217,6 @@ bool BaseFileManager::registerPackages(const Common::FSList &fslist) { } } } - debugC(kWinterMuteDebugFileAccess, " Registered %d files in %d package(s)", _files.size(), _packages.size()); return true; } @@ -278,177 +246,44 @@ bool BaseFileManager::registerPackages() { } } - debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Registered %d files in %d package(s)", _files.size(), _packages.size()); +// debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Registered %d files in %d package(s)", _files.size(), _packages.size()); return STATUS_OK; } bool BaseFileManager::registerPackage(Common::FSNode file, const Common::String &filename, bool searchSignature) { - uint32 absoluteOffset = 0; - bool boundToExe = false; - Common::SeekableReadStream * package = file.createReadStream(); - if (!package) - return STATUS_FAILED; - if (searchSignature) { - uint32 offset; - if (!findPackageSignature(package, &offset)) { - delete package; - return STATUS_OK; - } else { - package->seek(offset, SEEK_SET); - absoluteOffset = offset; - boundToExe = true; - } - } - - TPackageHeader hdr; - hdr.readFromStream(package); - if (hdr._magic1 != PACKAGE_MAGIC_1 || hdr._magic2 != PACKAGE_MAGIC_2 || hdr._packageVersion > PACKAGE_VERSION) { - debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Invalid header in package file '%s'. Ignoring.", filename.c_str()); - delete package; - return STATUS_OK; - } - - if (hdr._packageVersion != PACKAGE_VERSION) { - debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Warning: package file '%s' is outdated.", filename.c_str()); - } - - // new in v2 - if (hdr._packageVersion == PACKAGE_VERSION) { - uint32 dirOffset; - dirOffset = package->readUint32LE(); - dirOffset += absoluteOffset; - package->seek(dirOffset, SEEK_SET); - } - - for (uint32 i = 0; i < hdr._numDirs; i++) { - BasePackage *pkg = new BasePackage(this); - pkg->_fsnode = file; - if (!pkg) return STATUS_FAILED; - - pkg->_boundToExe = boundToExe; - - // read package info - byte nameLength = package->readByte(); - pkg->_name = new char[nameLength]; - package->read(pkg->_name, nameLength); - pkg->_cd = package->readByte(); - pkg->_priority = hdr._priority; - - if (!hdr._masterIndex) pkg->_cd = 0; // override CD to fixed disk - _packages.push_back(pkg); - - - // read file entries - uint32 numFiles = package->readUint32LE(); - - for (uint32 j = 0; j < numFiles; j++) { - char *name; - uint32 offset, length, compLength, flags, timeDate1, timeDate2; + PackageSet *pack = new PackageSet(file, filename, searchSignature); + _packages.add(file.getName(), pack, pack->getPriority() , true); - nameLength = package->readByte(); - name = new char[nameLength]; - package->read(name, nameLength); - - // v2 - xor name - if (hdr._packageVersion == PACKAGE_VERSION) { - for (int k = 0; k < nameLength; k++) { - ((byte *)name)[k] ^= 'D'; - } - } - debugC(kWinterMuteDebugFileAccess, "Package contains %s", name); - // some old version of ProjectMan writes invalid directory entries - // so at least prevent strupr from corrupting memory - name[nameLength - 1] = '\0'; - - Common::String upcName = name; - upcName.toUppercase(); - delete[] name; - name = NULL; - - offset = package->readUint32LE(); - offset += absoluteOffset; - length = package->readUint32LE(); - compLength = package->readUint32LE(); - flags = package->readUint32LE(); - - if (hdr._packageVersion == PACKAGE_VERSION) { - timeDate1 = package->readUint32LE(); - timeDate2 = package->readUint32LE(); - } - _filesIter = _files.find(upcName.c_str()); - if (_filesIter == _files.end()) { - BaseFileEntry *file = new BaseFileEntry(); - file->_package = pkg; - file->_offset = offset; - file->_length = length; - file->_compressedLength = compLength; - file->_flags = flags; - - _files[upcName.c_str()] = file; - } else { - // current package has lower CD number or higher priority, than the registered - if (pkg->_cd < _filesIter->_value->_package->_cd || pkg->_priority > _filesIter->_value->_package->_priority) { - _filesIter->_value->_package = pkg; - _filesIter->_value->_offset = offset; - _filesIter->_value->_length = length; - _filesIter->_value->_compressedLength = compLength; - _filesIter->_value->_flags = flags; - } - } - } - } - - - delete package; return STATUS_OK; } - ////////////////////////////////////////////////////////////////////////// -Common::File *BaseFileManager::openPackage(const Common::String &name) { - //TODO: Is it really necessary to do this when we have the ScummVM-system? - Common::File *ret = new Common::File(); - for (Common::FSList::iterator it = _packagePaths.begin(); it != _packagePaths.end(); it++) { - Common::String packageName = (*it).getName(); - if (packageName == (name + ".dcp")) - ret->open((*it)); - if (ret->isOpen()) { - return ret; - } - } - Common::String filename = Common::String::format("%s.%s", name.c_str(), PACKAGE_EXTENSION); - ret->open(filename); - if (ret->isOpen()) { - return ret; +Common::SeekableReadStream *BaseFileManager::openPkgFile(const Common::String &filename) { + Common::String upcName = filename; + upcName.toUppercase(); + Common::SeekableReadStream *file = NULL; + char fileName[MAX_PATH_LENGTH]; + strcpy(fileName, upcName.c_str()); + + // correct slashes + for (int32 i = 0; i < upcName.size(); i++) { + if (upcName[i] == '/') + upcName.setChar('\\', (uint32)i); } - warning("BaseFileManager::OpenPackage - Couldn't load file %s", name.c_str()); - delete ret; - return NULL; -} - - -////////////////////////////////////////////////////////////////////////// -BaseFileEntry *BaseFileManager::getPackageEntry(const Common::String &filename) { - Common::String upc_name = filename; - upc_name.toUppercase(); - - BaseFileEntry *ret = NULL; - _filesIter = _files.find(upc_name.c_str()); - if (_filesIter != _files.end()) ret = _filesIter->_value; - - return ret; + Common::ArchiveMemberPtr entry = _packages.getMember(upcName); + file = entry->createReadStream(); + return file; } bool BaseFileManager::hasFile(const Common::String &filename) { if (diskFileExists(filename)) return true; - if (getPackageEntry(filename)) + if (_packages.hasFile(filename)) return true; // We don't bother checking if the file can actually be opened, something bigger is wrong if that is the case. if (BaseResources::hasFile(filename)) return true; return false; - return true; } ////////////////////////////////////////////////////////////////////////// @@ -495,7 +330,7 @@ Common::SeekableReadStream *BaseFileManager::openFileRaw(const Common::String &f if (ret) return ret; - ret = openPkgFile(filename, this); + ret = openPkgFile(filename); if (ret) return ret; @@ -507,37 +342,4 @@ Common::SeekableReadStream *BaseFileManager::openFileRaw(const Common::String &f return NULL; } - -////////////////////////////////////////////////////////////////////////// -bool BaseFileManager::findPackageSignature(Common::SeekableReadStream *f, uint32 *offset) { - byte buf[32768]; - - byte signature[8]; - ((uint32 *)signature)[0] = PACKAGE_MAGIC_1; - ((uint32 *)signature)[1] = PACKAGE_MAGIC_2; - - uint32 fileSize = (uint32)f->size(); - uint32 startPos = 1024 * 1024; - uint32 bytesRead = startPos; - - while (bytesRead < fileSize - 16) { - uint32 toRead = MIN((unsigned int)32768, fileSize - bytesRead); - f->seek((int32)startPos, SEEK_SET); - uint32 actuallyRead = f->read(buf, toRead); - if (actuallyRead != toRead) return false; - - for (uint32 i = 0; i < toRead - 8; i++) - if (!memcmp(buf + i, signature, 8)) { - *offset = startPos + i; - return true; - } - - bytesRead = bytesRead + toRead - 16; - startPos = startPos + toRead - 16; - - } - return false; - -} - } // end of namespace WinterMute diff --git a/engines/wintermute/base/base_file_manager.h b/engines/wintermute/base/base_file_manager.h index 776fc32df1..89d1e5b46b 100644 --- a/engines/wintermute/base/base_file_manager.h +++ b/engines/wintermute/base/base_file_manager.h @@ -35,10 +35,7 @@ #include "common/file.h" namespace WinterMute { -class BaseFile; -class BaseFileEntry; class BaseGame; -class BasePackage; class BaseFileManager { public: bool cleanup(); @@ -55,10 +52,6 @@ public: bool saveFile(const Common::String &filename, byte *buffer, uint32 bufferSize, bool compressed = false, byte *prefixBuffer = NULL, uint32 prefixSize = 0); // Used only for detection bool registerPackages(const Common::FSList &fslist); - // Used by BasePackage only - BaseFileEntry *getPackageEntry(const Common::String &filename); - Common::File *openPackage(const Common::String &name); - bool requestCD(int cd, char *packageFile, const char *filename); private: typedef enum { PATH_PACKAGE, @@ -69,13 +62,12 @@ private: bool addPath(TPathType type, const Common::FSNode &path); bool registerPackages(); Common::SeekableReadStream *openFileRaw(const Common::String &filename); + Common::SeekableReadStream *openPkgFile(const Common::String &filename); Common::FSList _packagePaths; bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset); bool registerPackage(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); - Common::Array _packages; + Common::SearchSet _packages; Common::Array _openFiles; - Common::HashMap _files; - Common::HashMap::iterator _filesIter; // This class is intentionally not a subclass of Base, as it needs to be used by // the detector too, without launching the entire engine: BaseGame *_gameRef; diff --git a/engines/wintermute/base/file/BPkgFile.cpp b/engines/wintermute/base/file/BPkgFile.cpp deleted file mode 100644 index be37571901..0000000000 --- a/engines/wintermute/base/file/BPkgFile.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * This file is based on WME Lite. - * http://dead-code.org/redir.php?target=wmelite - * Copyright (c) 2011 Jan Nedoma - */ - -#include "engines/wintermute/base/file/base_package.h" -#include "engines/wintermute/base/file/BPkgFile.h" -#include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/base_file_manager.h" -#include "common/util.h" -#include "common/file.h" -#include "common/stream.h" -#include "common/substream.h" -#include "common/zlib.h" - -namespace WinterMute { - -// This file is only needed until the next merge/rebase, as wrapCompressedStream now can set a known size -// as such it is not renamed to follow the convention of the rest of the files. - -// HACK: wrapCompressedStream might set the size to 0, so we need a way to override it. -class CBPkgFile : public Common::SeekableReadStream { - uint32 _size; - Common::SeekableReadStream *_stream; -public: - CBPkgFile(Common::SeekableReadStream *stream, uint32 knownLength) : _size(knownLength), _stream(stream) {} - virtual ~CBPkgFile() { - delete _stream; - } - virtual uint32 read(void *dataPtr, uint32 dataSize) { - return _stream->read(dataPtr, dataSize); - } - virtual bool eos() const { - return _stream->eos(); - } - virtual int32 pos() const { - return _stream->pos(); - } - virtual int32 size() const { - return _size; - } - virtual bool seek(int32 offset, int whence = SEEK_SET) { - return _stream->seek(offset, whence); - } -}; - -Common::SeekableReadStream *openPkgFile(const Common::String &filename, BaseFileManager *fileManager) { - BaseFileEntry *fileEntry; - Common::SeekableReadStream *file = NULL; - char fileName[MAX_PATH_LENGTH]; - strcpy(fileName, filename.c_str()); - - // correct slashes - for (uint32 i = 0; i < strlen(fileName); i++) { - if (fileName[i] == '/') fileName[i] = '\\'; - } - - fileEntry = fileManager->getPackageEntry(fileName); - if (!fileEntry) return NULL; - - file = fileEntry->_package->getFilePointer(); - if (!file) return NULL; - - // TODO: Cleanup - bool compressed = (fileEntry->_compressedLength != 0); - /* _size = fileEntry->_length; */ - - if (compressed) { - // TODO: Really, most of this logic might be doable directly in the fileEntry? - // But for now, this should get us rolling atleast. - file = Common::wrapCompressedReadStream(new Common::SeekableSubReadStream(file, fileEntry->_offset, fileEntry->_offset + fileEntry->_length, DisposeAfterUse::YES)); - } else { - file = new Common::SeekableSubReadStream(file, fileEntry->_offset, fileEntry->_offset + fileEntry->_length, DisposeAfterUse::YES); - } - if (file->size() == 0) { - file = new CBPkgFile(file, fileEntry->_length); - } - - file->seek(0); - - return file; -} - -} // end of namespace WinterMute diff --git a/engines/wintermute/base/file/BPkgFile.h b/engines/wintermute/base/file/BPkgFile.h deleted file mode 100644 index ca1b1f837d..0000000000 --- a/engines/wintermute/base/file/BPkgFile.h +++ /dev/null @@ -1,49 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * This file is based on WME Lite. - * http://dead-code.org/redir.php?target=wmelite - * Copyright (c) 2011 Jan Nedoma - */ - -#ifndef WINTERMUTE_BPKGFILE_H -#define WINTERMUTE_BPKGFILE_H - -#include "engines/wintermute/base/file/base_file_entry.h" - -// This file is only needed until the next merge/rebase, as wrapCompressedStream now can set a known size -// as such it is not renamed to follow the convention of the rest of the files. - -namespace Common { -class SeekableReadStream; -class File; -} - -namespace WinterMute { - -class BaseFileManager; -Common::SeekableReadStream *openPkgFile(const Common::String &filename, BaseFileManager *fileManager); - -} // end of namespace WinterMute - -#endif diff --git a/engines/wintermute/base/file/base_file_entry.cpp b/engines/wintermute/base/file/base_file_entry.cpp index 43527f9fc0..5032650704 100644 --- a/engines/wintermute/base/file/base_file_entry.cpp +++ b/engines/wintermute/base/file/base_file_entry.cpp @@ -27,8 +27,61 @@ */ #include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/base_package.h" +#include "common/stream.h" +#include "common/substream.h" +#include "common/zlib.h" namespace WinterMute { +// HACK: wrapCompressedStream might set the size to 0, so we need a way to override it. +class CBPkgFile : public Common::SeekableReadStream { + uint32 _size; + Common::SeekableReadStream *_stream; +public: + CBPkgFile(Common::SeekableReadStream *stream, uint32 knownLength) : _size(knownLength), _stream(stream) {} + virtual ~CBPkgFile() { + delete _stream; + } + virtual uint32 read(void *dataPtr, uint32 dataSize) { + return _stream->read(dataPtr, dataSize); + } + virtual bool eos() const { + return _stream->eos(); + } + virtual int32 pos() const { + return _stream->pos(); + } + virtual int32 size() const { + return _size; + } + virtual bool seek(int32 offset, int whence = SEEK_SET) { + return _stream->seek(offset, whence); + } +}; + +Common::SeekableReadStream *BaseFileEntry::createReadStream() const { + Common::SeekableReadStream *file = _package->getFilePointer(); + if (!file) return NULL; + + // TODO: Cleanup + bool compressed = (_compressedLength != 0); + /* _size = fileEntry->_length; */ + + if (compressed) { + // TODO: Really, most of this logic might be doable directly in the fileEntry? + // But for now, this should get us rolling atleast. + file = Common::wrapCompressedReadStream(new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES)); + } else { + file = new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES); + } + if (file->size() == 0) { + file = new CBPkgFile(file, _length); + } + + file->seek(0); + + return file; +} ////////////////////////////////////////////////////////////////////////// BaseFileEntry::BaseFileEntry(){ diff --git a/engines/wintermute/base/file/base_file_entry.h b/engines/wintermute/base/file/base_file_entry.h index 086a70e7ed..b94a6cd9fc 100644 --- a/engines/wintermute/base/file/base_file_entry.h +++ b/engines/wintermute/base/file/base_file_entry.h @@ -29,14 +29,18 @@ #ifndef WINTERMUTE_BFILEENTRY_H #define WINTERMUTE_BFILEENTRY_H +#include "common/archive.h" #include "common/str.h" +#include "common/stream.h" namespace WinterMute { class BasePackage; -class BaseFileEntry { +class BaseFileEntry : public Common::ArchiveMember { public: + virtual Common::SeekableReadStream *createReadStream() const; + virtual Common::String getName() const { return _filename; } uint32 _timeDate2; uint32 _timeDate1; uint32 _flags; diff --git a/engines/wintermute/base/file/base_package.cpp b/engines/wintermute/base/file/base_package.cpp index 1706a7a50b..6987cad3e6 100644 --- a/engines/wintermute/base/file/base_package.cpp +++ b/engines/wintermute/base/file/base_package.cpp @@ -27,78 +27,229 @@ */ #include "engines/wintermute/base/file/base_package.h" -#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/dcpackage.h" +#include "engines/wintermute/wintermute.h" #include "common/file.h" #include "common/stream.h" +#include "common/debug.h" namespace WinterMute { -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////// -BasePackage::BasePackage(BaseFileManager *fileMan)/*: BaseClass(inGame) */{ - _file = NULL; - _name = NULL; +BasePackage::BasePackage() { + _name = ""; _cd = 0; _priority = 0; _boundToExe = false; - _fileManager = fileMan; } +Common::SeekableReadStream *BasePackage::getFilePointer() { + Common::SeekableReadStream *stream = _fsnode.createReadStream(); -////////////////////////////////////////////////////////////////////////// -BasePackage::~BasePackage() { - if (_name) delete [] _name; - closeFilePointer(_file); + return stream; } - -////////////////////////////////////////////////////////////////////////// -bool BasePackage::open() { - if (_file) return true; - else { - _file = getFilePointer(); - return _file ? true : false; +static bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset) { + byte buf[32768]; + + byte signature[8]; + ((uint32 *)signature)[0] = PACKAGE_MAGIC_1; + ((uint32 *)signature)[1] = PACKAGE_MAGIC_2; + + uint32 fileSize = (uint32)f->size(); + uint32 startPos = 1024 * 1024; + uint32 bytesRead = startPos; + + while (bytesRead < fileSize - 16) { + uint32 toRead = MIN((unsigned int)32768, fileSize - bytesRead); + f->seek((int32)startPos, SEEK_SET); + uint32 actuallyRead = f->read(buf, toRead); + if (actuallyRead != toRead) return false; + + for (uint32 i = 0; i < toRead - 8; i++) + if (!memcmp(buf + i, signature, 8)) { + *offset = startPos + i; + return true; + } + + bytesRead = bytesRead + toRead - 16; + startPos = startPos + toRead - 16; + } + return false; + } +PackageSet::PackageSet(Common::FSNode file, const Common::String &filename, bool searchSignature) { + uint32 absoluteOffset = 0; + _priority = 0; + bool boundToExe = false; + Common::SeekableReadStream *stream = file.createReadStream(); + if (!stream) + return; + if (searchSignature) { + uint32 offset; + if (!findPackageSignature(stream, &offset)) { + delete stream; + return; + } else { + stream->seek(offset, SEEK_SET); + absoluteOffset = offset; + boundToExe = true; + } + } + + TPackageHeader hdr; + hdr.readFromStream(stream); + if (hdr._magic1 != PACKAGE_MAGIC_1 || hdr._magic2 != PACKAGE_MAGIC_2 || hdr._packageVersion > PACKAGE_VERSION) { + debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Invalid header in package file '%s'. Ignoring.", filename.c_str()); + delete stream; + return; + } + + if (hdr._packageVersion != PACKAGE_VERSION) { + debugC(kWinterMuteDebugFileAccess | kWinterMuteDebugLog, " Warning: package file '%s' is outdated.", filename.c_str()); + } + _priority = hdr._priority; + // new in v2 + if (hdr._packageVersion == PACKAGE_VERSION) { + uint32 dirOffset; + dirOffset = stream->readUint32LE(); + dirOffset += absoluteOffset; + stream->seek(dirOffset, SEEK_SET); + } + assert(hdr._numDirs == 1); + for (uint32 i = 0; i < hdr._numDirs; i++) { + BasePackage *pkg = new BasePackage(); + if (!pkg) return; + pkg->_fsnode = file; + + pkg->_boundToExe = boundToExe; + + // read package info + byte nameLength = stream->readByte(); + char *pkgName = new char[nameLength]; + stream->read(pkgName, nameLength); + pkg->_name = pkgName; + pkg->_cd = stream->readByte(); + pkg->_priority = hdr._priority; + delete[] pkgName; + pkgName = NULL; + + if (!hdr._masterIndex) pkg->_cd = 0; // override CD to fixed disk + _packages.push_back(pkg); + + + // read file entries + uint32 numFiles = stream->readUint32LE(); + + for (uint32 j = 0; j < numFiles; j++) { + char *name; + uint32 offset, length, compLength, flags, timeDate1, timeDate2; + + nameLength = stream->readByte(); + name = new char[nameLength]; + stream->read(name, nameLength); + + // v2 - xor name + if (hdr._packageVersion == PACKAGE_VERSION) { + for (int k = 0; k < nameLength; k++) { + ((byte *)name)[k] ^= 'D'; + } + } + debugC(kWinterMuteDebugFileAccess, "Package contains %s", name); + warning( "Package contains %s", name); + // some old version of ProjectMan writes invalid directory entries + // so at least prevent strupr from corrupting memory + name[nameLength - 1] = '\0'; + + Common::String upcName = name; + upcName.toUppercase(); + delete[] name; + name = NULL; + + offset = stream->readUint32LE(); + offset += absoluteOffset; + length = stream->readUint32LE(); + compLength = stream->readUint32LE(); + flags = stream->readUint32LE(); + + if (hdr._packageVersion == PACKAGE_VERSION) { + timeDate1 = stream->readUint32LE(); + timeDate2 = stream->readUint32LE(); + } + _filesIter = _files.find(upcName.c_str()); + if (_filesIter == _files.end()) { + BaseFileEntry *fileEntry = new BaseFileEntry(); + fileEntry->_package = pkg; + fileEntry->_offset = offset; + fileEntry->_length = length; + fileEntry->_compressedLength = compLength; + fileEntry->_flags = flags; + + _files[upcName.c_str()] = Common::ArchiveMemberPtr(fileEntry); + } else { + // current package has higher priority than the registered + // TODO: This cast might be a bit ugly. + BaseFileEntry *filePtr = (BaseFileEntry*) &*(_filesIter->_value); + if (pkg->_priority > filePtr->_package->_priority) { + filePtr->_package = pkg; + filePtr->_offset = offset; + filePtr->_length = length; + filePtr->_compressedLength = compLength; + filePtr->_flags = flags; + } + } + } + } + debugC(kWinterMuteDebugFileAccess, " Registered %d files in %d package(s)", _files.size(), _packages.size()); + + delete stream; +} -////////////////////////////////////////////////////////////////////////// -bool BasePackage::close() { - delete _file; - _file = NULL; - return true; +PackageSet::~PackageSet() { + for (Common::Array::iterator it = _packages.begin(); it != _packages.end(); it++) { + delete *it; + } + _packages.clear(); } +bool PackageSet::hasFile(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap::const_iterator it; + it = _files.find(upcName.c_str()); + return (it != _files.end()); +} -////////////////////////////////////////////////////////////////////////// -bool BasePackage::read(Common::SeekableReadStream *file, uint32 offset, byte *buffer, uint32 size) { - bool ret; - if (!(ret = open())) return ret; - else { - if (file->seek(offset, SEEK_SET)) return false; - if (file->read(buffer, size) != 1) return false; - else return true; +int PackageSet::listMembers(Common::ArchiveMemberList &list) const { + Common::HashMap::const_iterator it = _files.begin(); + Common::HashMap::const_iterator end = _files.end(); + int count = 0; + for (; it != end; ++it) { + const Common::ArchiveMemberPtr ptr(it->_value); + list.push_back(ptr); + count++; } + return count; } -////////////////////////////////////////////////////////////////////////// -Common::SeekableReadStream *BasePackage::getFilePointer() { - Common::SeekableReadStream *stream = _fsnode.createReadStream(); -/* Common::File *file = _fileManager->openPackage(_name); - if (!file) { - _fileManager->requestCD(_cd, _name, ""); - file = _fileManager->openPackage(_name); - }*/ - return stream; +const Common::ArchiveMemberPtr PackageSet::getMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap::const_iterator it; + it = _files.find(upcName.c_str()); + return Common::ArchiveMemberPtr(it->_value); } -////////////////////////////////////////////////////////////////////////// -void BasePackage::closeFilePointer(Common::SeekableReadStream *&file) { - delete file; - file = NULL; +Common::SeekableReadStream *PackageSet::createReadStreamForMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap::const_iterator it; + it = _files.find(upcName.c_str()); + if (it != _files.end()) + return it->_value->createReadStream(); + return NULL; } } // end of namespace WinterMute diff --git a/engines/wintermute/base/file/base_package.h b/engines/wintermute/base/file/base_package.h index 673655a710..b9a7bc5934 100644 --- a/engines/wintermute/base/file/base_package.h +++ b/engines/wintermute/base/file/base_package.h @@ -29,32 +29,60 @@ #ifndef WINTERMUTE_BPACKAGE_H #define WINTERMUTE_BPACKAGE_H +#include "common/archive.h" #include "common/stream.h" #include "common/fs.h" -namespace Common { -class SeekableReadStream; -} - namespace WinterMute { -class BaseFileManager; class BasePackage { - BaseFileManager *_fileManager; public: Common::SeekableReadStream *getFilePointer(); - void closeFilePointer(Common::SeekableReadStream *&file); - Common::FSNode _fsnode; bool _boundToExe; byte _priority; - bool read(Common::SeekableReadStream *file, uint32 offset, byte *buffer, uint32 size); - bool close(); - bool open(); - char *_name; + Common::String _name; int _cd; - Common::SeekableReadStream *_file; - BasePackage(BaseFileManager *fileMan); - ~BasePackage(); + BasePackage(); +}; + +class PackageSet : public Common::Archive { +public: + virtual ~PackageSet(); + + PackageSet(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); + /** + * Check if a member with the given name is present in the Archive. + * Patterns are not allowed, as this is meant to be a quick File::exists() + * replacement. + */ + virtual bool hasFile(const Common::String &name) const; + + /** + * Add all members of the Archive to list. + * Must only append to list, and not remove elements from it. + * + * @return the number of names added to list + */ + virtual int listMembers(Common::ArchiveMemberList &list) const; + + /** + * Returns a ArchiveMember representation of the given file. + */ + virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + + /** + * Create a stream bound to a member with the specified name in the + * archive. If no member with this name exists, 0 is returned. + * @return the newly created input stream + */ + virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + + int getPriority() const { return _priority; } +private: + byte _priority; + Common::Array _packages; + Common::HashMap _files; + Common::HashMap::iterator _filesIter; }; } // end of namespace WinterMute diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk index 3ad30cf66b..02ba08f255 100644 --- a/engines/wintermute/module.mk +++ b/engines/wintermute/module.mk @@ -44,7 +44,6 @@ MODULE_OBJS := \ base/file/base_package.o \ base/file/base_resources.o \ base/file/base_save_thumb_file.o \ - base/file/BPkgFile.o \ base/font/base_font_bitmap.o \ base/font/base_font_truetype.o \ base/font/base_font.o \ -- cgit v1.2.3