/* 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/hashmap.h" #include "common/memstream.h" #include "common/debug.h" #include "common/file.h" #include "zvision/zfs_archive.h" namespace ZVision { ZfsArchive::ZfsArchive(const Common::String &fileName) : _fileName(fileName) { Common::File zfsFile; 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) 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