mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 01:08:25 +00:00
158 lines
4.9 KiB
C++
158 lines
4.9 KiB
C++
/* 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/archives/zfs_archive.h"
|
|
|
|
#include "common/memstream.h"
|
|
#include "common/debug.h"
|
|
#include "common/file.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
|
|
|
|
|