/* 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 "composer/resource.h" #include "common/debug.h" #include "common/memstream.h" #include "common/substream.h" #include "common/util.h" #include "common/textconsole.h" namespace Composer { // Base Archive code // (copied from clone2727's mohawk code) Archive::Archive() { _stream = 0; } Archive::~Archive() { close(); } bool Archive::openFile(const Common::String &fileName) { Common::File *file = new Common::File(); if (!file->open(fileName)) { delete file; return false; } if (!openStream(file)) { close(); return false; } return true; } void Archive::close() { _types.clear(); delete _stream; _stream = 0; } 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.matchString(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::getResourceFlags(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); const Resource &res = resMap[id]; return res.flags; } 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.matchString(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; } Common::Array Archive::getResourceTypeList() const { Common::Array typeList; for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) typeList.push_back(it->_key); return typeList; } Common::Array Archive::getResourceIDList(uint32 type) const { Common::Array idList; if (!_types.contains(type)) return idList; const ResourceMap &resMap = _types[type]; for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) idList.push_back(it->_key); return idList; } // Composer Archive code bool ComposerArchive::openStream(Common::SeekableReadStream *stream) { // Make sure no other file is open... close(); bool newStyle = false; uint32 headerSize = stream->readUint32LE(); if (headerSize == SWAP_CONSTANT_32(ID_LBRC)) { // new-style file newStyle = true; headerSize = stream->readUint32LE(); uint32 zeros = stream->readUint32LE(); if (zeros != 0) error("invalid LBRC header (%d instead of zeros)", zeros); } uint16 numResourceTypes = stream->readUint16LE(); if (newStyle) { uint16 unknown = stream->readUint16LE(); debug(4, "skipping unknown %04x", unknown); } debug(4, "Reading LBRC resource table with %d entries", numResourceTypes); for (uint i = 0; i < numResourceTypes; i++) { uint32 tag = stream->readUint32BE(); uint32 tableOffset = stream->readUint32LE(); debug(4, "Type '%s' at offset %d", tag2str(tag), tableOffset); // starting from the start of the resource table, which differs // according to whether we have the 10 extra bytes for newStyle if (newStyle) tableOffset += 16; else tableOffset += 6; ResourceMap &resMap = _types[tag]; uint32 oldPos = stream->pos(); stream->seek(tableOffset); while (true) { if (stream->eos()) error("LBRC file ran out of stream"); uint32 offset, size, id, flags; if (newStyle) { offset = stream->readUint32LE(); if (!offset) break; size = stream->readUint32LE(); id = stream->readUint16LE(); flags = stream->readUint16LE(); // set to 1 for preload, otherwise no preload /*uint32 junk = */ stream->readUint32LE(); } else { id = stream->readUint16LE(); if (!id) break; offset = stream->readUint32LE(); offset += headerSize; size = stream->readUint32LE(); flags = stream->readUint16LE(); // FIXME } Resource &res = resMap[id]; res.offset = offset; res.size = size; res.flags = flags; debug(4, "Id %d, offset %d, size %d, flags %08x", id, offset, size, flags); } stream->seek(oldPos); } _stream = stream; return true; } Pipe::Pipe(Common::SeekableReadStream *stream, uint16 id) { _offset = 0; _stream = stream; _anim = NULL; _pipeId = id; } Pipe::~Pipe() { } void Pipe::nextFrame() { if (_offset == (uint)_stream->size()) return; _stream->seek(_offset, SEEK_SET); uint32 tagCount = _stream->readUint32LE(); _offset += 4; for (uint i = 0; i < tagCount; i++) { uint32 tag = _stream->readUint32BE(); uint32 count = _stream->readUint32LE(); _offset += 8; ResourceMap &resMap = _types[tag]; _offset += (12 * count); for (uint j = 0; j < count; j++) { uint32 offset = _stream->readUint32LE(); uint32 size = _stream->readUint32LE(); uint16 id = _stream->readUint16LE(); uint32 unknown = _stream->readUint16LE(); // frame id? debug(9, "pipe: %s/%d: offset %d, size %d, unknown %d", tag2str(tag), id, offset, size, unknown); PipeResourceEntry entry; entry.size = size; entry.offset = _offset; resMap[id].entries.push_back(entry); _offset += size; } _stream->seek(_offset, SEEK_SET); } } bool Pipe::hasResource(uint32 tag, uint16 id) const { if (!_types.contains(tag)) return false; return _types[tag].contains(id); } Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffering) { if (!_types.contains(tag)) error("Pipe 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 PipeResource &res = resMap[id]; if (res.entries.size() == 1) { Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(_stream, res.entries[0].offset, res.entries[0].offset + res.entries[0].size); if (buffering) { _types[tag].erase(id); bool found = false; for (Common::List::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++) if ((*i) == id) found = true; if (!found) _bufferedResources[tag].push_back(id); } return stream; } // If there are multiple entries in the pipe, we have to concaternate them together. uint32 size = 0; for (uint i = 0; i < res.entries.size(); i++) size += res.entries[i].size; byte *buffer = (byte *)malloc(size); uint32 offset = 0; for (uint i = 0; i < res.entries.size(); i++) { _stream->seek(res.entries[i].offset, SEEK_SET); _stream->read(buffer + offset, res.entries[i].size); offset += res.entries[i].size; } if (buffering) { _types[tag].erase(id); bool found = false; for (Common::List::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++) if ((*i) == id) found = true; if (!found) _bufferedResources[tag].push_back(id); } return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); } OldPipe::OldPipe(Common::SeekableReadStream *stream, uint16 pipeId) : Pipe(stream, pipeId), _currFrame(0) { uint32 tag = _stream->readUint32BE(); if (tag != ID_PIPE) error("invalid tag for pipe (%08x)", tag); _numFrames = _stream->readUint32LE(); uint16 scriptCount = _stream->readUint16LE(); _scripts.reserve(scriptCount); for (uint i = 0; i < scriptCount; i++) _scripts.push_back(_stream->readUint16LE()); _offset = _stream->pos(); } void OldPipe::nextFrame() { if (_currFrame >= _numFrames) return; _stream->seek(_offset, SEEK_SET); uint32 tag = _stream->readUint32BE(); if (tag != ID_FRME) error("invalid tag for pipe (%08x)", tag); uint16 spriteCount = _stream->readUint16LE(); uint32 spriteSize = _stream->readUint32LE(); uint32 audioSize = _stream->readUint32LE(); Common::Array spriteIds; Common::Array spriteEntries; for (uint i = 0; i < spriteCount; i++) { spriteIds.push_back(_stream->readUint16LE()); PipeResourceEntry entry; entry.size = _stream->readUint32LE(); entry.offset = _stream->readUint32LE(); spriteEntries.push_back(entry); } uint32 spriteDataOffset = _stream->pos(); _stream->skip(spriteSize); ResourceMap &spriteResMap = _types[ID_BMAP]; spriteResMap.clear(); for (uint i = 0; i < spriteIds.size(); i++) { PipeResourceEntry &entry = spriteEntries[i]; entry.offset += spriteDataOffset; spriteResMap[spriteIds[i]].entries.push_back(entry); } ResourceMap &audioResMap = _types[ID_WAVE]; audioResMap.clear(); if (audioSize > 0) { PipeResourceEntry entry; entry.size = audioSize; entry.offset = _stream->pos(); // we use 0xffff for per-frame pipe audio audioResMap[0xffff].entries.push_back(entry); _stream->skip(audioSize); } _offset = _stream->pos(); _currFrame++; } } // End of namespace Composer