diff options
Diffstat (limited to 'engines/zvision/file')
-rw-r--r-- | engines/zvision/file/lzss_read_stream.cpp | 102 | ||||
-rw-r--r-- | engines/zvision/file/lzss_read_stream.h | 71 | ||||
-rw-r--r-- | engines/zvision/file/save_manager.cpp | 290 | ||||
-rw-r--r-- | engines/zvision/file/save_manager.h | 110 | ||||
-rw-r--r-- | engines/zvision/file/search_manager.cpp | 290 | ||||
-rw-r--r-- | engines/zvision/file/search_manager.h | 71 | ||||
-rw-r--r-- | engines/zvision/file/zfs_archive.cpp | 155 | ||||
-rw-r--r-- | engines/zvision/file/zfs_archive.h | 125 |
8 files changed, 1214 insertions, 0 deletions
diff --git a/engines/zvision/file/lzss_read_stream.cpp b/engines/zvision/file/lzss_read_stream.cpp new file mode 100644 index 0000000000..6f27eaa765 --- /dev/null +++ b/engines/zvision/file/lzss_read_stream.cpp @@ -0,0 +1,102 @@ +/* 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. + * + */ + +#include "common/scummsys.h" + +#include "zvision/file/lzss_read_stream.h" + +namespace ZVision { + +LzssReadStream::LzssReadStream(Common::SeekableReadStream *source) + : _source(source), + // It's convention to set the starting cursor position to blockSize - 16 + _windowCursor(0x0FEE), + _eosFlag(false) { + // Clear the window to null + memset(_window, 0, BLOCK_SIZE); +} + +uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) { + uint32 destinationCursor = 0; + + while (destinationCursor < numberOfBytes) { + byte flagbyte = _source->readByte(); + if (_source->eos()) + break; + uint mask = 1; + + for (int i = 0; i < 8; ++i) { + if ((flagbyte & mask) == mask) { + byte data = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + _window[_windowCursor] = data; + destination[destinationCursor++] = data; + + // Increment and wrap the window cursor + _windowCursor = (_windowCursor + 1) & 0xFFF; + } else { + byte low = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + byte high = _source->readByte(); + if (_source->eos()) { + return destinationCursor; + } + + uint16 length = (high & 0xF) + 2; + uint16 offset = low | ((high & 0xF0) << 4); + + for (int j = 0; j <= length; ++j) { + byte temp = _window[(offset + j) & 0xFFF]; + _window[_windowCursor] = temp; + destination[destinationCursor++] = temp; + _windowCursor = (_windowCursor + 1) & 0xFFF; + } + } + + mask = mask << 1; + } + } + + return destinationCursor; +} + +bool LzssReadStream::eos() const { + return _eosFlag; +} + +uint32 LzssReadStream::read(void *dataPtr, uint32 dataSize) { + uint32 bytesRead = decompressBytes(static_cast<byte *>(dataPtr), dataSize); + if (bytesRead < dataSize) { + // Flag that we're at EOS + _eosFlag = true; + } + + return dataSize; +} + +} // End of namespace ZVision diff --git a/engines/zvision/file/lzss_read_stream.h b/engines/zvision/file/lzss_read_stream.h new file mode 100644 index 0000000000..1420621f13 --- /dev/null +++ b/engines/zvision/file/lzss_read_stream.h @@ -0,0 +1,71 @@ +/* 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. + * + */ + +#ifndef ZVISION_LZSS_STREAM_H +#define ZVISION_LZSS_STREAM_H + +#include "common/stream.h" +#include "common/array.h" + +namespace Common { +class SeekableReadStream; +} + +namespace ZVision { + +class LzssReadStream : public Common::ReadStream { +public: + /** + * A class that decompresses LZSS data and implements ReadStream for easy access + * to the decompiled data. + * + * @param source The source data + */ + LzssReadStream(Common::SeekableReadStream *source); + +private: + enum { + BLOCK_SIZE = 0x1000 + }; + +private: + Common::SeekableReadStream *_source; + byte _window[BLOCK_SIZE]; + uint _windowCursor; + bool _eosFlag; + +public: + bool eos() const; + uint32 read(void *dataPtr, uint32 dataSize); + +private: + /** + * Decompress the next <numberOfBytes> from the source stream. Or until EOS + * + * @param numberOfBytes How many bytes to decompress. This is a count of source bytes, not destination bytes + */ + uint32 decompressBytes(byte *destination, uint32 numberOfBytes); +}; + +} + +#endif diff --git a/engines/zvision/file/save_manager.cpp b/engines/zvision/file/save_manager.cpp new file mode 100644 index 0000000000..042fafd38e --- /dev/null +++ b/engines/zvision/file/save_manager.cpp @@ -0,0 +1,290 @@ +/* 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. + * + */ + +#include "common/scummsys.h" + +#include "zvision/zvision.h" +#include "zvision/file/save_manager.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" + +#include "common/system.h" +#include "common/translation.h" + +#include "graphics/surface.h" +#include "graphics/thumbnail.h" + +#include "gui/message.h" +#include "gui/saveload.h" + +namespace ZVision { + +const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G'); + +bool SaveManager::scummVMSaveLoadDialog(bool isSave) { + GUI::SaveLoadChooser *dialog; + Common::String desc; + int slot; + + if (isSave) { + dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + + slot = dialog->runModalWithCurrentTarget(); + desc = dialog->getResultString(); + + if (desc.empty()) { + // create our own description for the saved game, the user didnt enter it + desc = dialog->createDefaultSaveDescription(slot); + } + + if (desc.size() > 28) + desc = Common::String(desc.c_str(), 28); + } else { + dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); + slot = dialog->runModalWithCurrentTarget(); + } + + delete dialog; + + if (slot < 0) + return false; + + if (isSave) { + saveGame(slot, desc); + return true; + } else { + Common::ErrorCode result = loadGame(slot).getCode(); + return (result == Common::kNoError); + } +} + +void SaveManager::saveGame(uint slot, const Common::String &saveName) { + Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); + Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); + + writeSaveGameHeader(file, saveName); + + _engine->getScriptManager()->serialize(file); + + file->finalize(); + delete file; + + _lastSaveTime = g_system->getMillis(); +} + +void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { + Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); + Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); + + writeSaveGameHeader(file, saveName); + + file->write(stream->getData(), stream->size()); + + file->finalize(); + delete file; + + _lastSaveTime = g_system->getMillis(); +} + +void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { + if (_tempSave) { + saveGame(slot, saveName, _tempSave); + flushSaveBuffer(); + } +} + +void SaveManager::autoSave() { + saveGame(0, "Auto save"); +} + +void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { + file->writeUint32BE(SAVEGAME_ID); + + // Write version + file->writeByte(SAVE_VERSION); + + // Write savegame name + file->writeString(saveName); + file->writeByte(0); + + // Create a thumbnail and save it + Graphics::saveThumbnail(*file); + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + file->writeSint16LE(td.tm_year + 1900); + file->writeSint16LE(td.tm_mon + 1); + file->writeSint16LE(td.tm_mday); + file->writeSint16LE(td.tm_hour); + file->writeSint16LE(td.tm_min); +} + +Common::Error SaveManager::loadGame(uint slot) { + Common::SeekableReadStream *saveFile = getSlotFile(slot); + if (saveFile == 0) { + return Common::kPathDoesNotExist; + } + + // Read the header + SaveGameHeader header; + if (!readSaveGameHeader(saveFile, header)) { + return Common::kUnknownError; + } + + ScriptManager *scriptManager = _engine->getScriptManager(); + // Update the state table values + scriptManager->deserialize(saveFile); + + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; + + return Common::kNoError; +} + +Common::Error SaveManager::loadGame(const Common::String &saveName) { + Common::File *saveFile = _engine->getSearchManager()->openFile(saveName); + if (saveFile == NULL) { + saveFile = new Common::File; + if (!saveFile->open(saveName)) { + delete saveFile; + return Common::kPathDoesNotExist; + } + } + + // Read the header + SaveGameHeader header; + if (!readSaveGameHeader(saveFile, header)) { + return Common::kUnknownError; + } + + ScriptManager *scriptManager = _engine->getScriptManager(); + // Update the state table values + scriptManager->deserialize(saveFile); + + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; + + return Common::kNoError; +} + +bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { + uint32 tag = in->readUint32BE(); + // Check if it's original savegame than fill header structure + if (tag == MKTAG('Z', 'N', 'S', 'G')) { + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.saveName = "Original Save"; + header.thumbnail = NULL; + header.version = SAVE_ORIGINAL; + in->seek(-4, SEEK_CUR); + return true; + } + if (tag != SAVEGAME_ID) { + warning("File is not a ZVision save file. Aborting load"); + return false; + } + + // Read in the version + header.version = in->readByte(); + + // Check that the save version isn't newer than this binary + if (header.version > SAVE_VERSION) { + uint tempVersion = header.version; + GUI::MessageDialog dialog( + Common::String::format( + "This save file uses version %u, but this engine only " + "supports up to version %d. You will need an updated version " + "of the engine to use this save file.", tempVersion, SAVE_VERSION + ), + "OK"); + dialog.runModal(); + } + + // Read in the save name + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { + Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + if (saveFile == NULL) { + // Try to load standard save file + Common::String filename; + if (_engine->getGameId() == GID_GRANDINQUISITOR) + filename = Common::String::format("inqsav%u.sav", slot); + else if (_engine->getGameId() == GID_NEMESIS) + filename = Common::String::format("nemsav%u.sav", slot); + + saveFile = _engine->getSearchManager()->openFile(filename); + if (saveFile == NULL) { + Common::File *tmpFile = new Common::File; + if (!tmpFile->open(filename)) { + delete tmpFile; + } else { + saveFile = tmpFile; + } + } + + } + + return saveFile; +} + +void SaveManager::prepareSaveBuffer() { + if (_tempSave) + delete _tempSave; + + _tempSave = new Common::MemoryWriteStreamDynamic; + + _engine->getScriptManager()->serialize(_tempSave); +} + +void SaveManager::flushSaveBuffer() { + if (_tempSave) + delete _tempSave; + + _tempSave = NULL; +} + +} // End of namespace ZVision diff --git a/engines/zvision/file/save_manager.h b/engines/zvision/file/save_manager.h new file mode 100644 index 0000000000..fc8db67566 --- /dev/null +++ b/engines/zvision/file/save_manager.h @@ -0,0 +1,110 @@ +/* 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. + * + */ + +#ifndef ZVISION_SAVE_MANAGER_H +#define ZVISION_SAVE_MANAGER_H + +#include "common/savefile.h" +#include "common/memstream.h" + +namespace Common { +class String; +} + +namespace Graphics { +struct Surface; +} + +namespace ZVision { + +class ZVision; + +struct SaveGameHeader { + byte version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; +}; + +class SaveManager { +public: + SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _lastSaveTime(0) {} + ~SaveManager() { + flushSaveBuffer(); + } + + uint32 getLastSaveTime() const { + return _lastSaveTime; + } + +private: + ZVision *_engine; + uint32 _lastSaveTime; + static const uint32 SAVEGAME_ID; + + enum { + SAVE_ORIGINAL = 0, + SAVE_VERSION = 1 + }; + + Common::MemoryWriteStreamDynamic *_tempSave; + +public: + /** + * Called every room change. Saves the state of the room just before + * the room changes. + */ + void autoSave(); + /** + * Copies the data from the last auto-save into a new save file. We + * can't use the current state data because the save menu *IS* a room. + * The file is named using ZVision::generateSaveFileName(slot) + * + * @param slot The save slot this save pertains to. Must be [1, 20] + * @param saveName The internal name for this save. This is NOT the name of the actual save file. + */ + void saveGame(uint slot, const Common::String &saveName); + void saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream); + void saveGameBuffered(uint slot, const Common::String &saveName); + /** + * Loads the state data from the save file that slot references. Uses + * ZVision::generateSaveFileName(slot) to get the save file name. + * + * @param slot The save slot to load. Must be [1, 20] + */ + Common::Error loadGame(uint slot); + Common::Error loadGame(const Common::String &saveName); + + Common::SeekableReadStream *getSlotFile(uint slot); + bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); + + void prepareSaveBuffer(); + void flushSaveBuffer(); + bool scummVMSaveLoadDialog(bool isSave); +private: + void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/file/search_manager.cpp b/engines/zvision/file/search_manager.cpp new file mode 100644 index 0000000000..ec250ff648 --- /dev/null +++ b/engines/zvision/file/search_manager.cpp @@ -0,0 +1,290 @@ +/* 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. +* +*/ + +#include "common/debug.h" +#include "common/fs.h" +#include "common/stream.h" + +#include "zvision/file/search_manager.h" +#include "zvision/file/zfs_archive.h" + +namespace ZVision { + +SearchManager::SearchManager(const Common::String &rootPath, int depth) { + _root = rootPath; + if (_root[_root.size() - 1] == '\\' || _root[_root.size() - 1] == '/') + _root.deleteLastChar(); + + Common::FSNode fsNode(_root); + + listDirRecursive(_dirList, fsNode, depth); + + for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end();) { + if ((*it).hasSuffix("\\") || (*it).hasSuffix("/")) + (*it).deleteLastChar(); + + if (it->size() == _root.size()) + it = _dirList.erase(it); + else if (it->size() > _root.size()) { + *it = Common::String(it->c_str() + _root.size() + 1); + it++; + } else + it++; + } +} + +SearchManager::~SearchManager() { + Common::List<Common::Archive *>::iterator it = _archList.begin(); + while (it != _archList.end()) { + delete *it; + it++; + } + + _archList.clear(); +} + +void SearchManager::addPatch(const Common::String &src, const Common::String &dst) { + Common::String lowerCaseName = dst; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator it = _files.find(lowerCaseName); + + if (it != _files.end()) { + lowerCaseName = src; + lowerCaseName.toLowercase(); + _files[lowerCaseName] = it->_value; + } +} + +void SearchManager::addFile(const Common::String &name, Common::Archive *arch) { + bool addArch = true; + Common::List<Common::Archive *>::iterator it = _archList.begin(); + while (it != _archList.end()) { + if (*it == arch) { + addArch = false; + break; + } + it++; + } + if (addArch) + _archList.push_back(arch); + + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::Node nod; + nod.name = lowerCaseName; + nod.arch = arch; + + SearchManager::MatchList::iterator fit = _files.find(lowerCaseName); + + if (fit == _files.end()) { + _files[lowerCaseName] = nod; + } else { + Common::SeekableReadStream *stream = fit->_value.arch->createReadStreamForMember(fit->_value.name); + if (stream) { + if (stream->size() < 10) + fit->_value.arch = arch; + delete stream; + } else { + _files[lowerCaseName] = nod; + } + } +} + +Common::File *SearchManager::openFile(const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = _files.find(lowerCaseName); + + if (fit != _files.end()) { + Common::File *tmp = new Common::File(); + tmp->open(fit->_value.name, *fit->_value.arch); + return tmp; + } + return NULL; +} + +bool SearchManager::openFile(Common::File &file, const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = _files.find(lowerCaseName); + + if (fit != _files.end()) + return file.open(fit->_value.name, *fit->_value.arch); + return false; +} + +bool SearchManager::hasFile(const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = _files.find(lowerCaseName); + + if (fit != _files.end()) + return true; + return false; +} + +void SearchManager::loadZix(const Common::String &name) { + Common::File file; + file.open(name); + + Common::String line; + + while (!file.eos()) { + line = file.readLine(); + if (line.matchString("----------*", true)) + break; + } + + if (file.eos()) + return; + + Common::Array<Common::Archive *> archives; + + while (!file.eos()) { + line = file.readLine(); + line.trim(); + if (line.matchString("----------*", true)) + break; + else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true)) { + Common::String path(line.c_str() + 5); + // Check if INQUIS.ZIX refers to the ZGI folder, and check the game + // root folder instead + if (path.hasPrefix("zgi\\")) + path = Common::String(path.c_str() + 4); + + Common::Archive *arc; + char tempPath[128]; + strcpy(tempPath, path.c_str()); + for (uint i = 0; i < path.size(); i++) + if (tempPath[i] == '\\') + tempPath[i] = '/'; + + path = Common::String(tempPath); + if (path.size() && path[0] == '.') + path.deleteChar(0); + if (path.size() && path[0] == '/') + path.deleteChar(0); + + if (path.matchString("*.zfs", true)) + arc = new ZfsArchive(path); + else { + if (path.size()) { + if (path[path.size() - 1] == '\\' || path[path.size() - 1] == '/') + path.deleteLastChar(); + if (path.size()) + for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) + if (path.equalsIgnoreCase(*it)) { + path = *it; + break; + } + } + + path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); + + arc = new Common::FSDirectory(path); + } + archives.push_back(arc); + } + } + + if (file.eos()) + return; + + while (!file.eos()) { + line = file.readLine(); + line.trim(); + uint dr = 0; + char buf[32]; + if (sscanf(line.c_str(), "%u %s", &dr, buf) == 2) { + if (dr <= archives.size() && dr > 0) { + addFile(Common::String(buf), archives[dr - 1]); + } + } + } +} + +void SearchManager::addDir(const Common::String &name) { + Common::String path; + for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) + if (name.equalsIgnoreCase(*it)) { + path = *it; + break; + } + + if (path.size() == 0) + return; + + path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); + + Common::FSDirectory *dir = new Common::FSDirectory(path); + + Common::ArchiveMemberList list; + dir->listMatchingMembers(list, "*.zfs"); + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + Common::String flname = (*iter)->getName(); + + ZfsArchive *zfs = new ZfsArchive(Common::String::format("%s/%s", name.c_str(), flname.c_str())); + + Common::ArchiveMemberList zfslist; + zfs->listMembers(zfslist); + + for (Common::ArchiveMemberList::iterator ziter = zfslist.begin(); ziter != zfslist.end(); ++ziter) { + Common::String zfsFileName = (*ziter)->getName(); + addFile(zfsFileName, zfs); + } + } + + list.clear(); + dir->listMembers(list); + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + Common::String flname = (*iter)->getName(); + addFile(flname, dir); + } +} + +void SearchManager::listDirRecursive(Common::List<Common::String> &_list, const Common::FSNode &fsNode, int depth) { + Common::FSList fsList; + if (fsNode.getChildren(fsList)) { + + _list.push_back(fsNode.getPath()); + + if (depth > 1) + for (Common::FSList::const_iterator it = fsList.begin(); it != fsList.end(); ++it) + listDirRecursive(_list, *it, depth - 1); + } +} + +void SearchManager::listMembersWithExtension(MatchList &fileList, Common::String extension) { + for (SearchManager::MatchList::iterator it = _files.begin(); it != _files.end(); ++it) { + if (it->_key.hasSuffix(extension)) + fileList[it->_key] = it->_value; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/file/search_manager.h b/engines/zvision/file/search_manager.h new file mode 100644 index 0000000000..b9ed02ec13 --- /dev/null +++ b/engines/zvision/file/search_manager.h @@ -0,0 +1,71 @@ +/* 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. + * + */ + +#ifndef ZVISION_SEARCH_MANAGER_H +#define ZVISION_SEARCH_MANAGER_H + +#include "common/str.h" +#include "common/hash-str.h" +#include "common/hashmap.h" +#include "common/archive.h" +#include "common/file.h" +#include "common/list.h" + +namespace ZVision { + +class SearchManager { +public: + SearchManager(const Common::String &rootPath, int depth); + ~SearchManager(); + + void addFile(const Common::String &name, Common::Archive *arch); + void addDir(const Common::String &name); + void addPatch(const Common::String &src, const Common::String &dst); + + Common::File *openFile(const Common::String &name); + bool openFile(Common::File &file, const Common::String &name); + bool hasFile(const Common::String &name); + + void loadZix(const Common::String &name); + + struct Node { + Common::String name; + Common::Archive *arch; + }; + + typedef Common::HashMap<Common::String, Node, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MatchList; + + void listMembersWithExtension(MatchList &fileList, Common::String extension); + +private: + + void listDirRecursive(Common::List<Common::String> &dirList, const Common::FSNode &fsNode, int depth); + + Common::List<Common::String> _dirList; + Common::List<Common::Archive *> _archList; + Common::String _root; + MatchList _files; +}; + +} + +#endif // ZVISION_SEARCH_MANAGER_H diff --git a/engines/zvision/file/zfs_archive.cpp b/engines/zvision/file/zfs_archive.cpp new file mode 100644 index 0000000000..3a385cd8fd --- /dev/null +++ b/engines/zvision/file/zfs_archive.cpp @@ -0,0 +1,155 @@ +/* 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. + * + */ + +#include "common/scummsys.h" +#include "common/memstream.h" +#include "common/debug.h" +#include "common/file.h" + +#include "zvision/file/zfs_archive.h" + +namespace ZVision { + +ZfsArchive::ZfsArchive(const Common::String &fileName) : _fileName(fileName) { + Common::File zfsFile; + memset(&_header, 0, sizeof(_header)); + + if (!zfsFile.open(_fileName)) { + warning("ZFSArchive::ZFSArchive(): Could not find the archive file"); + return; + } + + readHeaders(&zfsFile); + + debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); +} + +ZfsArchive::ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream) : _fileName(fileName) { + readHeaders(stream); + + debug(1, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.c_str(), _entryHeaders.size()); +} + +ZfsArchive::~ZfsArchive() { + debug(1, "ZfsArchive Destructor Called"); + ZfsEntryHeaderMap::iterator it = _entryHeaders.begin(); + for (; it != _entryHeaders.end(); ++it) { + delete it->_value; + } +} + +void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) { + // Don't do a straight struct cast since we can't guarantee endianness + _header.magic = stream->readUint32LE(); + _header.unknown1 = stream->readUint32LE(); + _header.maxNameLength = stream->readUint32LE(); + _header.filesPerBlock = stream->readUint32LE(); + _header.fileCount = stream->readUint32LE(); + _header.xorKey[0] = stream->readByte(); + _header.xorKey[1] = stream->readByte(); + _header.xorKey[2] = stream->readByte(); + _header.xorKey[3] = stream->readByte(); + _header.fileSectionOffset = stream->readUint32LE(); + + uint32 nextOffset; + + do { + // Read the offset to the next block + nextOffset = stream->readUint32LE(); + + // Read in each entry header + for (uint32 i = 0; i < _header.filesPerBlock; ++i) { + ZfsEntryHeader entryHeader; + + entryHeader.name = readEntryName(stream); + entryHeader.offset = stream->readUint32LE(); + entryHeader.id = stream->readUint32LE(); + entryHeader.size = stream->readUint32LE(); + entryHeader.time = stream->readUint32LE(); + entryHeader.unknown = stream->readUint32LE(); + + if (entryHeader.size != 0) + _entryHeaders[entryHeader.name] = new ZfsEntryHeader(entryHeader); + } + + // Seek to the next block of headers + stream->seek(nextOffset); + } while (nextOffset != 0); +} + +Common::String ZfsArchive::readEntryName(Common::SeekableReadStream *stream) const { + // Entry Names are at most 16 bytes and are null padded + char buffer[16]; + stream->read(buffer, 16); + + return Common::String(buffer); +} + +bool ZfsArchive::hasFile(const Common::String &name) const { + return _entryHeaders.contains(name); +} + +int ZfsArchive::listMembers(Common::ArchiveMemberList &list) const { + int matches = 0; + + for (ZfsEntryHeaderMap::const_iterator it = _entryHeaders.begin(); it != _entryHeaders.end(); ++it) { + list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_value->name, this))); + matches++; + } + + return matches; +} + +const Common::ArchiveMemberPtr ZfsArchive::getMember(const Common::String &name) const { + if (!_entryHeaders.contains(name)) + return Common::ArchiveMemberPtr(); + + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *ZfsArchive::createReadStreamForMember(const Common::String &name) const { + if (!_entryHeaders.contains(name)) { + return 0; + } + + ZfsEntryHeader *entryHeader = _entryHeaders[name]; + + Common::File zfsArchive; + zfsArchive.open(_fileName); + zfsArchive.seek(entryHeader->offset); + + // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory + byte *buffer = (byte *)malloc(entryHeader->size); + zfsArchive.read(buffer, entryHeader->size); + // Decrypt the data in place + if (_header.xorKey[0] + _header.xorKey[1] + _header.xorKey[2] + _header.xorKey[3] != 0) + unXor(buffer, entryHeader->size, _header.xorKey); + + return new Common::MemoryReadStream(buffer, entryHeader->size, DisposeAfterUse::YES); +} + +void ZfsArchive::unXor(byte *buffer, uint32 length, const byte *xorKey) const { + for (uint32 i = 0; i < length; ++i) + buffer[i] ^= xorKey[i % 4]; +} + +} // End of namespace ZVision diff --git a/engines/zvision/file/zfs_archive.h b/engines/zvision/file/zfs_archive.h new file mode 100644 index 0000000000..fe0221416d --- /dev/null +++ b/engines/zvision/file/zfs_archive.h @@ -0,0 +1,125 @@ +/* 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. + * + */ + +#ifndef ZVISION_ZFS_ARCHIVE_H +#define ZVISION_ZFS_ARCHIVE_H + +#include "common/archive.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + +namespace Common { +class String; +} + +namespace ZVision { + +struct ZfsHeader { + uint32 magic; + uint32 unknown1; + uint32 maxNameLength; + uint32 filesPerBlock; + uint32 fileCount; + uint8 xorKey[4]; + uint32 fileSectionOffset; +}; + +struct ZfsEntryHeader { + Common::String name; + uint32 offset; + uint32 id; + uint32 size; + uint32 time; + uint32 unknown; +}; + +typedef Common::HashMap<Common::String, ZfsEntryHeader *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ZfsEntryHeaderMap; + +class ZfsArchive : public Common::Archive { +public: + ZfsArchive(const Common::String &fileName); + ZfsArchive(const Common::String &fileName, Common::SeekableReadStream *stream); + ~ZfsArchive(); + + /** + * 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. + */ + bool hasFile(const Common::String &fileName) 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 + */ + int listMembers(Common::ArchiveMemberList &list) const; + + /** + * Returns a ArchiveMember representation of the given file. + */ + 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 + */ + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + const Common::String _fileName; + ZfsHeader _header; + ZfsEntryHeaderMap _entryHeaders; + + /** + * Parses the zfs file into file entry headers that can be used later + * to get the entry data. + * + * @param stream The contents of the zfs file + */ + void readHeaders(Common::SeekableReadStream *stream); + + /** + * Entry names are contained within a 16 byte block. This reads the block + * and converts it the name to a Common::String + * + * @param stream The zfs file stream + * @return The entry file name + */ + Common::String readEntryName(Common::SeekableReadStream *stream) const; + + /** + * ZFS file entries can be encrypted using XOR encoding. This method + * decodes the buffer in place using the supplied xorKey. + * + * @param buffer The data to decode + * @param length Length of buffer + */ + void unXor(byte *buffer, uint32 length, const byte *xorKey) const; +}; + +} // End of namespace ZVision + +#endif |