MOHAWK: Rework archive handling

A new base class has been introduced (aptly named "Archive"), which is much cleaner than inheriting from MohawkArchive. In addition, the underlying resource retrieving code has been merged to reduce dupliplication.
This commit is contained in:
Matthew Hoops 2011-06-28 11:58:52 -04:00
parent ede71596ba
commit 71c4329d86
10 changed files with 357 additions and 497 deletions

View file

@ -217,7 +217,7 @@ void MohawkEngine_CSTime::nextScene() {
void MohawkEngine_CSTime::loadResourceFile(Common::String name) {
MohawkArchive *archive = new MohawkArchive();
if (!archive->open(name + ".mhk"))
if (!archive->openFile(name + ".mhk"))
error("failed to open %s.mhk", name.c_str());
_mhk.push_back(archive);
}

View file

@ -234,7 +234,7 @@ LivingBooksCursorManager_v2::LivingBooksCursorManager_v2() {
// Try to open the system archive if we have it
_sysArchive = new MohawkArchive();
if (!_sysArchive->open("system.mhk")) {
if (!_sysArchive->openFile("system.mhk")) {
delete _sysArchive;
_sysArchive = 0;
}

View file

@ -82,7 +82,7 @@ LBPage::LBPage(MohawkEngine_LivingBooks *vm) : _vm(vm) {
_cascade = false;
}
void LBPage::open(MohawkArchive *mhk, uint16 baseId) {
void LBPage::open(Archive *mhk, uint16 baseId) {
_mhk = mhk;
_baseId = baseId;
@ -385,8 +385,8 @@ bool MohawkEngine_LivingBooks::loadPage(LBMode mode, uint page, uint subpage) {
warning("ignoring 'killgag' for filename '%s'", filename.c_str());
}
MohawkArchive *pageArchive = createMohawkArchive();
if (!filename.empty() && pageArchive->open(filename)) {
Archive *pageArchive = createArchive();
if (!filename.empty() && pageArchive->openFile(filename)) {
_page = new LBPage(this);
_page->open(pageArchive, 1000);
} else {
@ -590,11 +590,11 @@ void MohawkEngine_LivingBooks::updatePage() {
}
}
void MohawkEngine_LivingBooks::addArchive(MohawkArchive *archive) {
void MohawkEngine_LivingBooks::addArchive(Archive *archive) {
_mhk.push_back(archive);
}
void MohawkEngine_LivingBooks::removeArchive(MohawkArchive *archive) {
void MohawkEngine_LivingBooks::removeArchive(Archive *archive) {
for (uint i = 0; i < _mhk.size(); i++) {
if (archive != _mhk[i])
continue;
@ -863,8 +863,11 @@ Common::String MohawkEngine_LivingBooks::convertWinFileName(const Common::String
return filename;
}
MohawkArchive *MohawkEngine_LivingBooks::createMohawkArchive() const {
return isPreMohawk() ? new LivingBooksArchive_v1() : new MohawkArchive();
Archive *MohawkEngine_LivingBooks::createArchive() const {
if (isPreMohawk())
return new LivingBooksArchive_v1();
return new MohawkArchive();
}
bool MohawkEngine_LivingBooks::isPreMohawk() const {
@ -3855,8 +3858,8 @@ void LBProxyItem::init() {
}
debug(1, "LBProxyItem loading archive '%s' with id %d", filename.c_str(), baseId);
MohawkArchive *pageArchive = _vm->createMohawkArchive();
if (!pageArchive->open(filename))
Archive *pageArchive = _vm->createArchive();
if (!pageArchive->openFile(filename))
error("failed to open archive '%s' (for proxy '%s')", filename.c_str(), _desc.c_str());
_page = new LBPage(_vm);
_page->open(pageArchive, baseId);

View file

@ -628,7 +628,7 @@ public:
LBPage(MohawkEngine_LivingBooks *vm);
~LBPage();
void open(MohawkArchive *mhk, uint16 baseId);
void open(Archive *mhk, uint16 baseId);
uint16 getResourceVersion();
void itemDestroyed(LBItem *item);
@ -638,7 +638,7 @@ public:
protected:
MohawkEngine_LivingBooks *_vm;
MohawkArchive *_mhk;
Archive *_mhk;
Common::Array<LBItem *> _items;
uint16 _baseId;
@ -667,8 +667,8 @@ public:
Common::Rect readRect(Common::ReadStreamEndian *stream);
GUI::Debugger *getDebugger() { return _console; }
void addArchive(MohawkArchive *archive);
void removeArchive(MohawkArchive *Archive);
void addArchive(Archive *archive);
void removeArchive(Archive *Archive);
void addItem(LBItem *item);
void removeItems(const Common::Array<LBItem *> &items);
@ -697,7 +697,7 @@ public:
// helper functions, also used by LBProxyItem
Common::String getFileNameFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover);
MohawkArchive *createMohawkArchive() const;
Archive *createArchive() const;
private:
LivingBooksConsole *_console;

View file

@ -77,7 +77,7 @@ enum MohawkGameFeatures {
struct MohawkGameDescription;
class Sound;
class PauseDialog;
class MohawkArchive;
class Archive;
class CursorManager;
class MohawkEngine : public ::Engine {
@ -123,7 +123,7 @@ private:
protected:
// An array holding the main Mohawk archives require by the games
Common::Array<MohawkArchive *> _mhk;
Common::Array<Archive *> _mhk;
};
} // End of namespace Mohawk

View file

@ -281,7 +281,7 @@ Common::Error MohawkEngine_Myst::run() {
// Load Help System (Masterpiece Edition Only)
if (getFeatures() & GF_ME) {
MohawkArchive *mhk = new MohawkArchive();
if (!mhk->open("help.dat"))
if (!mhk->openFile("help.dat"))
error("Could not load help.dat");
_mhk.push_back(mhk);
}
@ -488,7 +488,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
_mhk[0] = new MohawkArchive();
}
if (!_mhk[0]->open(mystFiles[_curStack]))
if (!_mhk[0]->openFile(mystFiles[_curStack]))
error("Could not open %s", mystFiles[_curStack]);
if (getPlatform() == Common::kPlatformMacintosh)

View file

@ -29,23 +29,25 @@
namespace Mohawk {
MohawkArchive::MohawkArchive() {
_mhk = NULL;
_types = NULL;
_fileTable = NULL;
// Base Archive code
Archive::Archive() {
_stream = 0;
}
bool MohawkArchive::open(const Common::String &filename) {
Archive::~Archive() {
close();
}
bool Archive::openFile(const Common::String &fileName) {
Common::File *file = new Common::File();
if (!file->open(filename)) {
if (!file->open(fileName)) {
delete file;
return false;
}
_curFile = filename;
if (!open(file)) {
if (!openStream(file)) {
close();
return false;
}
@ -53,431 +55,349 @@ bool MohawkArchive::open(const Common::String &filename) {
return true;
}
void MohawkArchive::close() {
delete _mhk; _mhk = NULL;
delete[] _types; _types = NULL;
delete[] _fileTable; _fileTable = NULL;
_curFile.clear();
void Archive::close() {
_types.clear();
delete _stream; _stream = 0;
}
bool MohawkArchive::open(Common::SeekableReadStream *stream) {
bool Archive::hasResource(uint32 tag, uint16 id) const {
if (!_types.contains(tag))
return false;
return _types[tag].contains(id);
}
bool Archive::hasResource(uint32 tag, const Common::String &resName) const {
if (!_types.contains(tag) || resName.empty())
return false;
const ResourceMap &resMap = _types[tag];
for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
if (it->_value.name.equalsIgnoreCase(resName))
return true;
return false;
}
Common::SeekableReadStream *Archive::getResource(uint32 tag, uint16 id) {
if (!_types.contains(tag))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
const ResourceMap &resMap = _types[tag];
if (!resMap.contains(id))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
const Resource &res = resMap[id];
return new Common::SeekableSubReadStream(_stream, res.offset, res.offset + res.size);
}
uint32 Archive::getOffset(uint32 tag, uint16 id) const {
if (!_types.contains(tag))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
const ResourceMap &resMap = _types[tag];
if (!resMap.contains(id))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
return resMap[id].offset;
}
uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const {
if (!_types.contains(tag) || resName.empty())
return 0xFFFF;
const ResourceMap &resMap = _types[tag];
for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
if (it->_value.name.equalsIgnoreCase(resName))
return it->_key;
return 0xFFFF;
}
Common::String Archive::getName(uint32 tag, uint16 id) const {
if (!_types.contains(tag))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
const ResourceMap &resMap = _types[tag];
if (!resMap.contains(id))
error("Archive does not contain '%s' %04x", tag2str(tag), id);
return resMap[id].name;
}
// Mohawk Archive code
struct FileTableEntry {
uint32 offset;
uint32 size;
byte flags;
uint16 unknown;
};
struct NameTableEntry {
uint16 index;
Common::String name;
};
bool MohawkArchive::openStream(Common::SeekableReadStream *stream) {
// Make sure no other file is open...
close();
_mhk = stream;
if (_mhk->readUint32BE() != ID_MHWK) {
if (stream->readUint32BE() != ID_MHWK) {
warning("Could not find tag 'MHWK'");
return false;
}
/* uint32 fileSize = */ _mhk->readUint32BE();
/* uint32 fileSize = */ stream->readUint32BE();
if (_mhk->readUint32BE() != ID_RSRC) {
if (stream->readUint32BE() != ID_RSRC) {
warning("Could not find tag \'RSRC\'");
return false;
}
_rsrc.version = _mhk->readUint16BE();
uint16 version = stream->readUint16BE();
if (_rsrc.version != 0x100) {
warning("Unsupported Mohawk resource version %d.%d", (_rsrc.version >> 8) & 0xff, _rsrc.version & 0xff);
if (version != 0x100) {
warning("Unsupported Mohawk resource version %d.%d", (version >> 8) & 0xff, version & 0xff);
return false;
}
_rsrc.compaction = _mhk->readUint16BE(); // Only used in creation, not in reading
_rsrc.filesize = _mhk->readUint32BE();
_rsrc.abs_offset = _mhk->readUint32BE();
_rsrc.file_table_offset = _mhk->readUint16BE();
_rsrc.file_table_size = _mhk->readUint16BE();
/* uint16 compaction = */ stream->readUint16BE(); // Only used in creation, not in reading
/* uint32 rsrcSize = */ stream->readUint32BE();
uint32 absOffset = stream->readUint32BE();
uint16 fileTableOffset = stream->readUint16BE();
/* uint16 fileTableSize = */ stream->readUint16BE();
debug (3, "Absolute Offset = %08x", _rsrc.abs_offset);
// First, read in the file table
stream->seek(absOffset + fileTableOffset);
Common::Array<FileTableEntry> fileTable;
fileTable.resize(stream->readUint32BE());
/////////////////////////////////
//Resource Dir
/////////////////////////////////
debug(4, "Reading file table with %d entries", fileTable.size());
// Type Table
_mhk->seek(_rsrc.abs_offset);
_typeTable.name_offset = _mhk->readUint16BE();
_typeTable.resource_types = _mhk->readUint16BE();
debug (0, "Name List Offset = %04x Number of Resource Types = %04x", _typeTable.name_offset, _typeTable.resource_types);
_types = new Type[_typeTable.resource_types];
for (uint16 i = 0; i < _typeTable.resource_types; i++) {
_types[i].tag = _mhk->readUint32BE();
_types[i].resource_table_offset = _mhk->readUint16BE();
_types[i].name_table_offset = _mhk->readUint16BE();
// HACK: Zoombini's SND resource starts will a NULL.
if (_types[i].tag == ID_SND)
debug (3, "Type[%02d]: Tag = \'SND\' ResTable Offset = %04x NameTable Offset = %04x", i, _types[i].resource_table_offset, _types[i].name_table_offset);
else
debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset, _types[i].name_table_offset);
// Resource Table
_mhk->seek(_rsrc.abs_offset + _types[i].resource_table_offset);
_types[i].resTable.resources = _mhk->readUint16BE();
debug (3, "Resources = %04x", _types[i].resTable.resources);
_types[i].resTable.entries = new Type::ResourceTable::Entries[_types[i].resTable.resources];
for (uint16 j = 0; j < _types[i].resTable.resources; j++) {
_types[i].resTable.entries[j].id = _mhk->readUint16BE();
_types[i].resTable.entries[j].index = _mhk->readUint16BE();
debug (4, "Entry[%02x]: ID = %04x (%d) Index = %04x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].index);
}
// Name Table
_mhk->seek(_rsrc.abs_offset + _types[i].name_table_offset);
_types[i].nameTable.num = _mhk->readUint16BE();
debug (3, "Names = %04x", _types[i].nameTable.num);
_types[i].nameTable.entries = new Type::NameTable::Entries[_types[i].nameTable.num];
for (uint16 j = 0; j < _types[i].nameTable.num; j++) {
_types[i].nameTable.entries[j].offset = _mhk->readUint16BE();
_types[i].nameTable.entries[j].index = _mhk->readUint16BE();
debug (4, "Entry[%02x]: Name List Offset = %04x Index = %04x", j, _types[i].nameTable.entries[j].offset, _types[i].nameTable.entries[j].index);
// Name List
uint32 pos = _mhk->pos();
_mhk->seek(_rsrc.abs_offset + _typeTable.name_offset + _types[i].nameTable.entries[j].offset);
char c = (char)_mhk->readByte();
while (c != 0) {
_types[i].nameTable.entries[j].name += c;
c = (char)_mhk->readByte();
}
debug (3, "Name = \'%s\'", _types[i].nameTable.entries[j].name.c_str());
// Get back to next entry
_mhk->seek(pos);
}
// Return to next TypeTable entry
_mhk->seek(_rsrc.abs_offset + (i + 1) * 8 + 4);
debug (3, "\n");
}
_mhk->seek(_rsrc.abs_offset + _rsrc.file_table_offset);
_fileTableAmount = _mhk->readUint32BE();
_fileTable = new FileTable[_fileTableAmount];
for (uint32 i = 0; i < _fileTableAmount; i++) {
_fileTable[i].offset = _mhk->readUint32BE();
_fileTable[i].dataSize = _mhk->readUint16BE();
_fileTable[i].dataSize += _mhk->readByte() << 16; // Get bits 15-24 of dataSize too
_fileTable[i].flags = _mhk->readByte();
_fileTable[i].unk = _mhk->readUint16BE();
for (uint32 i = 0; i < fileTable.size(); i++) {
fileTable[i].offset = stream->readUint32BE();
fileTable[i].size = stream->readUint16BE();
fileTable[i].size += stream->readByte() << 16; // Get bits 15-24 of size too
fileTable[i].flags = stream->readByte();
fileTable[i].unknown = stream->readUint16BE();
// Add in another 3 bits for file size from the flags.
// The flags are useless to us except for doing this ;)
_fileTable[i].dataSize += (_fileTable[i].flags & 7) << 24;
fileTable[i].size += (fileTable[i].flags & 7) << 24;
debug (4, "File[%02x]: Offset = %08x DataSize = %07x Flags = %02x Unk = %04x", i, _fileTable[i].offset, _fileTable[i].dataSize, _fileTable[i].flags, _fileTable[i].unk);
debug(4, "File[%02x]: Offset = %08x Size = %07x Flags = %02x Unknown = %04x", i, fileTable[i].offset, fileTable[i].size, fileTable[i].flags, fileTable[i].unknown);
}
// Now go in an read in each of the types
stream->seek(absOffset);
uint16 stringTableOffset = stream->readUint16BE();
uint16 typeCount = stream->readUint16BE();
debug(0, "Name List Offset = %04x Number of Resource Types = %04x", stringTableOffset, typeCount);
for (uint16 i = 0; i < typeCount; i++) {
uint32 tag = stream->readUint32BE();
uint16 resourceTableOffset = stream->readUint16BE();
uint16 nameTableOffset = stream->readUint16BE();
// HACK: Zoombini's SND resource starts will a NULL.
if (tag == ID_SND)
debug(3, "Type[%02d]: Tag = \'SND\' ResTable Offset = %04x NameTable Offset = %04x", i, resourceTableOffset, nameTableOffset);
else
debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(tag), resourceTableOffset, nameTableOffset);
// Name Table
stream->seek(absOffset + nameTableOffset);
Common::Array<NameTableEntry> nameTable;
nameTable.resize(stream->readUint16BE());
debug(3, "Names = %04x", nameTable.size());
for (uint16 j = 0; j < nameTable.size(); j++) {
uint16 offset = stream->readUint16BE();
nameTable[j].index = stream->readUint16BE();
debug(4, "Entry[%02x]: Name List Offset = %04x Index = %04x", j, offset, nameTable[j].index);
// Name List
uint32 pos = stream->pos();
stream->seek(absOffset + stringTableOffset + offset);
char c = (char)stream->readByte();
while (c != 0) {
nameTable[j].name += c;
c = (char)stream->readByte();
}
debug(3, "Name = \'%s\'", nameTable[j].name.c_str());
// Get back to next entry
stream->seek(pos);
}
// Resource Table
stream->seek(absOffset + resourceTableOffset);
uint16 resourceCount = stream->readUint16BE();
debug(3, "Resource count = %04x", resourceCount);
ResourceMap &resMap = _types[tag];
for (uint16 j = 0; j < resourceCount; j++) {
uint16 id = stream->readUint16BE();
uint16 index = stream->readUint16BE();
Resource &res = resMap[id];
// Pull out the name from the name table
for (uint32 k = 0; k < nameTable.size(); k++) {
if (nameTable[k].index == index) {
res.name = nameTable[k].name;
break;
}
}
// Pull out our offset/size too
res.offset = fileTable[index - 1].offset;
// WORKAROUND: tMOV resources pretty much ignore the size part of the file table,
// as the original just passed the full Mohawk file to QuickTime and the offset.
// We need to do this because of the way Mohawk is set up (this is much more "proper"
// than passing _stream at the right offset). We may want to do that in the future, though.
if (tag == ID_TMOV) {
if (index == fileTable.size() - 1)
res.size = stream->size() - fileTable[index - 1].offset;
else
res.size = fileTable[index].offset - fileTable[index - 1].offset;
} else
res.size = fileTable[index - 1].size;
debug(4, "Entry[%02x]: ID = %04x (%d) Index = %04x", j, id, id, index);
}
// Return to next TypeTable entry
stream->seek(absOffset + (i + 1) * 8 + 4);
debug(3, "\n");
}
_stream = stream;
return true;
}
int MohawkArchive::getTypeIndex(uint32 tag) {
for (uint16 i = 0; i < _typeTable.resource_types; i++)
if (_types[i].tag == tag)
return i;
return -1; // not found
}
// Living Books Archive code
int MohawkArchive::getIDIndex(int typeIndex, uint16 id) {
for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
if (_types[typeIndex].resTable.entries[i].id == id)
return i;
return -1; // not found
}
int MohawkArchive::getIDIndex(int typeIndex, const Common::String &resName) {
int index = -1;
for (uint16 i = 0; i < _types[typeIndex].nameTable.num; i++)
if (_types[typeIndex].nameTable.entries[i].name.matchString(resName)) {
index = _types[typeIndex].nameTable.entries[i].index;
break;
}
if (index < 0)
return -1; // Not found
for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
if (_types[typeIndex].resTable.entries[i].index == index)
return i;
return -1; // Not found
}
uint16 MohawkArchive::findResourceID(uint32 type, const Common::String &resName) {
int typeIndex = getTypeIndex(type);
if (typeIndex < 0)
return 0xFFFF;
int idIndex = getIDIndex(typeIndex, resName);
if (idIndex < 0)
return 0xFFFF;
return _types[typeIndex].resTable.entries[idIndex].id;
}
bool MohawkArchive::hasResource(uint32 tag, uint16 id) {
if (!_mhk)
return false;
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
return false;
return getIDIndex(typeIndex, id) >= 0;
}
bool MohawkArchive::hasResource(uint32 tag, const Common::String &resName) {
if (!_mhk)
return false;
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
return false;
return getIDIndex(typeIndex, resName) >= 0;
}
Common::String MohawkArchive::getName(uint32 tag, uint16 id) {
if (!_mhk)
return 0;
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
return 0;
int16 idIndex = -1;
for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
if (_types[typeIndex].resTable.entries[i].id == id) {
idIndex = _types[typeIndex].resTable.entries[i].index;
break;
}
assert(idIndex >= 0);
for (uint16 i = 0; i < _types[typeIndex].nameTable.num; i++)
if (_types[typeIndex].nameTable.entries[i].index == idIndex)
return _types[typeIndex].nameTable.entries[i].name;
return 0; // not found
}
uint32 MohawkArchive::getOffset(uint32 tag, uint16 id) {
assert(_mhk);
int16 typeIndex = getTypeIndex(tag);
assert(typeIndex >= 0);
int16 idIndex = getIDIndex(typeIndex, id);
assert(idIndex >= 0);
return _fileTable[_types[typeIndex].resTable.entries[idIndex].index - 1].offset;
}
Common::SeekableReadStream *MohawkArchive::getResource(uint32 tag, uint16 id) {
if (!_mhk)
error("MohawkArchive::getResource(): No File in Use");
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
error("Could not find a tag of '%s' in file '%s'", tag2str(tag), _curFile.c_str());
int16 idIndex = getIDIndex(typeIndex, id);
if (idIndex < 0)
error("Could not find '%s' %04x in file '%s'", tag2str(tag), id, _curFile.c_str());
// Note: the fileTableIndex is based off 1, not 0. So, subtract 1
uint16 fileTableIndex = _types[typeIndex].resTable.entries[idIndex].index - 1;
// WORKAROUND: tMOV resources pretty much ignore the size part of the file table,
// as the original just passed the full Mohawk file to QuickTime and the offset.
// We need to do this because of the way Mohawk is set up (this is much more "proper"
// than passing _mhk at the right offset). We may want to do that in the future, though.
if (_types[typeIndex].tag == ID_TMOV) {
if (fileTableIndex == _fileTableAmount - 1)
return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _mhk->size());
else
return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _fileTable[fileTableIndex + 1].offset);
}
return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _fileTable[fileTableIndex].offset + _fileTable[fileTableIndex].dataSize);
}
bool LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) {
bool LivingBooksArchive_v1::openStream(Common::SeekableReadStream *stream) {
close();
_mhk = stream;
// This is for the "old" Mohawk resource format used in some older
// Living Books. It is very similar, just missing the MHWK tag and
// some other minor differences, especially with the file table
// being merged into the resource table.
uint32 headerSize = _mhk->readUint32BE();
uint32 headerSize = stream->readUint32BE();
// NOTE: There are differences besides endianness! (Subtle changes,
// but different).
if (headerSize == 6) { // We're in Big Endian mode (Macintosh)
_mhk->readUint16BE(); // Resource Table Size
_typeTable.resource_types = _mhk->readUint16BE();
_types = new OldType[_typeTable.resource_types];
stream->readUint16BE(); // Resource Table Size
uint16 typeCount = stream->readUint16BE();
debug (0, "Old Mohawk File (Macintosh): Number of Resource Types = %04x", _typeTable.resource_types);
debug(0, "Old Mohawk File (Macintosh): Number of Resource Types = %04x", typeCount);
for (uint16 i = 0; i < _typeTable.resource_types; i++) {
_types[i].tag = _mhk->readUint32BE();
_types[i].resource_table_offset = (uint16)_mhk->readUint32BE() + 6;
_mhk->readUint32BE(); // Unknown (always 0?)
for (uint16 i = 0; i < typeCount; i++) {
uint32 tag = stream->readUint32BE();
uint32 resourceTableOffset = stream->readUint32BE() + 6;
stream->readUint32BE(); // Unknown (always 0?)
debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset);
debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset);
uint32 oldPos = _mhk->pos();
uint32 oldPos = stream->pos();
// Resource Table/File Table
_mhk->seek(_types[i].resource_table_offset);
_types[i].resTable.resources = _mhk->readUint16BE();
_types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources];
stream->seek(resourceTableOffset);
uint16 resourceCount = stream->readUint16BE();
for (uint16 j = 0; j < _types[i].resTable.resources; j++) {
_types[i].resTable.entries[j].id = _mhk->readUint16BE();
_types[i].resTable.entries[j].offset = _mhk->readUint32BE();
_types[i].resTable.entries[j].size = _mhk->readByte() << 16;
_types[i].resTable.entries[j].size += _mhk->readUint16BE();
_mhk->skip(5); // Unknown (always 0?)
ResourceMap &resMap = _types[tag];
debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size);
for (uint16 j = 0; j < resourceCount; j++) {
uint16 id = stream->readUint16BE();
Resource &res = resMap[id];
res.offset = stream->readUint32BE();
res.size = stream->readByte() << 16;
res.size |= stream->readUint16BE();
stream->skip(5); // Unknown (always 0?)
debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size);
}
_mhk->seek(oldPos);
debug (3, "\n");
stream->seek(oldPos);
debug(3, "\n");
}
} else if (SWAP_BYTES_32(headerSize) == 6) { // We're in Little Endian mode (Windows)
_mhk->readUint16LE(); // Resource Table Size
_typeTable.resource_types = _mhk->readUint16LE();
_types = new OldType[_typeTable.resource_types];
stream->readUint16LE(); // Resource Table Size
uint16 typeCount = stream->readUint16LE();
debug (0, "Old Mohawk File (Windows): Number of Resource Types = %04x", _typeTable.resource_types);
debug(0, "Old Mohawk File (Windows): Number of Resource Types = %04x", typeCount);
for (uint16 i = 0; i < _typeTable.resource_types; i++) {
_types[i].tag = _mhk->readUint32LE();
_types[i].resource_table_offset = _mhk->readUint16LE() + 6;
_mhk->readUint16LE(); // Unknown (always 0?)
for (uint16 i = 0; i < typeCount; i++) {
uint32 tag = stream->readUint32LE();
uint16 resourceTableOffset = stream->readUint16LE() + 6;
stream->readUint16LE(); // Unknown (always 0?)
debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset);
debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset);
uint32 oldPos = _mhk->pos();
uint32 oldPos = stream->pos();
// Resource Table/File Table
_mhk->seek(_types[i].resource_table_offset);
_types[i].resTable.resources = _mhk->readUint16LE();
_types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources];
stream->seek(resourceTableOffset);
uint16 resourceCount = stream->readUint16LE();
for (uint16 j = 0; j < _types[i].resTable.resources; j++) {
_types[i].resTable.entries[j].id = _mhk->readUint16LE();
_types[i].resTable.entries[j].offset = _mhk->readUint32LE();
_types[i].resTable.entries[j].size = _mhk->readUint32LE();
_mhk->readUint16LE(); // Unknown (always 0?)
ResourceMap &resMap = _types[tag];
debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size);
for (uint16 j = 0; j < resourceCount; j++) {
uint16 id = stream->readUint16LE();
Resource &res = resMap[id];
res.offset = stream->readUint32LE();
res.size = stream->readUint32LE();
stream->readUint16LE(); // Unknown (always 0?)
debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size);
}
_mhk->seek(oldPos);
debug (3, "\n");
stream->seek(oldPos);
debug(3, "\n");
}
} else {
warning("Could not determine type of Old Mohawk resource");
// Not a valid Living Books Archive
return false;
}
_stream = stream;
return true;
}
uint32 LivingBooksArchive_v1::getOffset(uint32 tag, uint16 id) {
assert(_mhk);
int16 typeIndex = getTypeIndex(tag);
assert(typeIndex >= 0);
int16 idIndex = getIDIndex(typeIndex, id);
assert(idIndex >= 0);
return _types[typeIndex].resTable.entries[idIndex].offset;
}
Common::SeekableReadStream *LivingBooksArchive_v1::getResource(uint32 tag, uint16 id) {
if (!_mhk)
error("LivingBooksArchive_v1::getResource(): No File in Use");
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
error("Could not find a tag of \'%s\' in file \'%s\'", tag2str(tag), _curFile.c_str());
int16 idIndex = getIDIndex(typeIndex, id);
if (idIndex < 0)
error("Could not find \'%s\' %04x in file \'%s\'", tag2str(tag), id, _curFile.c_str());
return new Common::SeekableSubReadStream(_mhk, _types[typeIndex].resTable.entries[idIndex].offset, _types[typeIndex].resTable.entries[idIndex].offset + _types[typeIndex].resTable.entries[idIndex].size);
}
bool LivingBooksArchive_v1::hasResource(uint32 tag, uint16 id) {
if (!_mhk)
return false;
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
return false;
return getIDIndex(typeIndex, id) >= 0;
}
int LivingBooksArchive_v1::getTypeIndex(uint32 tag) {
for (uint16 i = 0; i < _typeTable.resource_types; i++)
if (_types[i].tag == tag)
return i;
return -1; // not found
}
int LivingBooksArchive_v1::getIDIndex(int typeIndex, uint16 id) {
for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
if (_types[typeIndex].resTable.entries[i].id == id)
return i;
return -1; // not found
}
// DOS Archive (v2) code
// Partially based on the Prince of Persia Format Specifications
// See http://sdfg.com.ar/git/?p=fp-git.git;a=blob;f=FP/doc/FormatSpecifications
// However, I'm keeping with the terminology we've been using with the
// later archive formats.
bool DOSArchive_v2::open(Common::SeekableReadStream *stream) {
bool DOSArchive_v2::openStream(Common::SeekableReadStream *stream) {
close();
uint32 typeTableOffset = stream->readUint32LE();
@ -488,36 +408,38 @@ bool DOSArchive_v2::open(Common::SeekableReadStream *stream) {
stream->seek(typeTableOffset);
_typeTable.resource_types = stream->readUint16LE();
_types = new OldType[_typeTable.resource_types];
uint16 typeCount = stream->readUint16LE();
for (uint16 i = 0; i < _typeTable.resource_types; i++) {
_types[i].tag = stream->readUint32LE();
_types[i].resource_table_offset = stream->readUint16LE();
for (uint16 i = 0; i < typeCount; i++) {
uint32 tag = stream->readUint32LE();
uint16 resourceTableOffset = stream->readUint16LE();
debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset);
debug(3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x", i, tag2str(tag), resourceTableOffset);
uint32 oldPos = stream->pos();
// Resource Table/File Table
stream->seek(_types[i].resource_table_offset + typeTableOffset);
_types[i].resTable.resources = stream->readUint16LE();
_types[i].resTable.entries = new OldType::ResourceTable::Entries[_types[i].resTable.resources];
stream->seek(resourceTableOffset + typeTableOffset);
uint16 resourceCount = stream->readUint16LE();
for (uint16 j = 0; j < _types[i].resTable.resources; j++) {
_types[i].resTable.entries[j].id = stream->readUint16LE();
_types[i].resTable.entries[j].offset = stream->readUint32LE() + 1; // Need to add one to the offset to skip the checksum byte
_types[i].resTable.entries[j].size = stream->readUint16LE();
stream->skip(3); // Skip the useless flags
ResourceMap &resMap = _types[tag];
debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size);
for (uint16 j = 0; j < resourceCount; j++) {
uint16 id = stream->readUint16LE();
Resource &res = resMap[id];
res.offset = stream->readUint32LE() + 1; // Need to add one to the offset to skip the checksum byte
res.size = stream->readUint16LE();
stream->skip(3); // Skip (useless) flags
debug(4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, id, id, res.offset, res.size);
}
stream->seek(oldPos);
debug (3, "\n");
debug(3, "\n");
}
_mhk = stream;
_stream = stream;
return true;
}

View file

@ -22,6 +22,7 @@
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/hashmap.h"
#include "common/file.h"
#include "common/str.h"
@ -128,127 +129,61 @@ namespace Mohawk {
#define ID_BBOX MKTAG('B','B','O','X') // Boxes? (CSWorld, CSAmtrak)
#define ID_SYSX MKTAG('S','Y','S','X') // MIDI Sysex
struct FileTable {
uint32 offset;
uint32 dataSize; // Really 27 bits
byte flags; // Mostly useless except for the bottom 3 bits which are part of the size
uint16 unk; // Always 0
};
struct Type {
Type() { resTable.entries = NULL; nameTable.entries = NULL; }
~Type() { delete[] resTable.entries; delete[] nameTable.entries; }
//Type Table
uint32 tag;
uint16 resource_table_offset;
uint16 name_table_offset;
struct ResourceTable {
uint16 resources;
struct Entries {
uint16 id;
uint16 index;
} *entries;
} resTable;
struct NameTable {
uint16 num;
struct Entries {
uint16 offset;
uint16 index;
// Name List
Common::String name;
} *entries;
} nameTable;
};
struct TypeTable {
uint16 name_offset;
uint16 resource_types;
};
struct RSRC_Header {
uint16 version;
uint16 compaction;
uint32 filesize;
uint32 abs_offset;
uint16 file_table_offset;
uint16 file_table_size;
};
class MohawkArchive {
class Archive {
public:
MohawkArchive();
virtual ~MohawkArchive() { close(); }
Archive();
virtual ~Archive();
bool open(const Common::String &filename);
virtual bool open(Common::SeekableReadStream *stream);
bool openFile(const Common::String &fileName);
virtual bool openStream(Common::SeekableReadStream *stream) = 0;
void close();
virtual bool hasResource(uint32 tag, uint16 id);
virtual bool hasResource(uint32 tag, const Common::String &resName);
virtual Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
virtual uint32 getOffset(uint32 tag, uint16 id);
virtual uint16 findResourceID(uint32 type, const Common::String &resName);
Common::String getName(uint32 tag, uint16 id);
bool isOpen() const { return _stream != 0; }
bool hasResource(uint32 tag, uint16 id) const;
bool hasResource(uint32 tag, const Common::String &resName) const;
Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
uint32 getOffset(uint32 tag, uint16 id) const;
uint16 findResourceID(uint32 tag, const Common::String &resName) const;
Common::String getName(uint32 tag, uint16 id) const;
protected:
Common::SeekableReadStream *_mhk;
TypeTable _typeTable;
Common::String _curFile;
Common::SeekableReadStream *_stream;
private:
RSRC_Header _rsrc;
Type *_types;
FileTable *_fileTable;
uint16 _nameTableAmount;
uint16 _resourceTableAmount;
uint16 _fileTableAmount;
struct Resource {
uint32 offset;
uint32 size;
Common::String name;
};
int getTypeIndex(uint32 tag);
int getIDIndex(int typeIndex, uint16 id);
int getIDIndex(int typeIndex, const Common::String &resName);
typedef Common::HashMap<uint16, Resource> ResourceMap;
typedef Common::HashMap<uint32, ResourceMap> TypeMap;
TypeMap _types;
};
class LivingBooksArchive_v1 : public MohawkArchive {
class MohawkArchive : public Archive {
public:
LivingBooksArchive_v1() : MohawkArchive() {}
MohawkArchive() : Archive() {}
~MohawkArchive() {}
bool openStream(Common::SeekableReadStream *stream);
};
class LivingBooksArchive_v1 : public Archive {
public:
LivingBooksArchive_v1() : Archive() {}
~LivingBooksArchive_v1() {}
bool hasResource(uint32 tag, uint16 id);
bool hasResource(uint32 tag, const Common::String &resName) { return false; }
virtual bool open(Common::SeekableReadStream *stream);
Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
Common::SeekableReadStream *getResource(uint32 tag, const Common::String &resName) { return 0; }
uint32 getOffset(uint32 tag, uint16 id);
uint16 findResourceID(uint32 type, const Common::String &resName) { return 0xFFFF; }
protected:
struct OldType {
uint32 tag;
uint16 resource_table_offset;
struct ResourceTable {
uint16 resources;
struct Entries {
uint16 id;
uint32 offset;
uint32 size;
} *entries;
} resTable;
} *_types;
private:
int getTypeIndex(uint32 tag);
int getIDIndex(int typeIndex, uint16 id);
bool openStream(Common::SeekableReadStream *stream);
};
class DOSArchive_v2 : public LivingBooksArchive_v1 {
class DOSArchive_v2 : public Archive {
public:
DOSArchive_v2() : LivingBooksArchive_v1() {}
DOSArchive_v2() : Archive() {}
~DOSArchive_v2() {}
virtual bool open(Common::SeekableReadStream *stream);
bool openStream(Common::SeekableReadStream *stream);
};
} // End of namespace Mohawk

View file

@ -143,7 +143,7 @@ Common::Error MohawkEngine_Riven::run() {
_extrasFile = new MohawkArchive();
// We need extras.mhk for inventory images, marble images, and credits images
if (!_extrasFile->open("extras.mhk")) {
if (!_extrasFile->openFile("extras.mhk")) {
Common::String message = "You're missing 'extras.mhk'. Using the 'arcriven.z' installer file also works.";
GUIErrorMessage(message);
warning("%s", message.c_str());
@ -317,7 +317,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
Common::String filename = Common::String(prefix) + endings[i];
MohawkArchive *mhk = new MohawkArchive();
if (mhk->open(filename))
if (mhk->openFile(filename))
_mhk.push_back(mhk);
else
delete mhk;

View file

@ -100,7 +100,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
MohawkArchive *mhk = new MohawkArchive();
if (!mhk->open(loadFile)) {
if (!mhk->openStream(loadFile)) {
warning("Save file is not a Mohawk archive");
delete mhk;
return false;