aboutsummaryrefslogtreecommitdiff
path: root/engines/composer/resource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/composer/resource.cpp')
-rw-r--r--engines/composer/resource.cpp337
1 files changed, 337 insertions, 0 deletions
diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp
new file mode 100644
index 0000000000..b40bdb379b
--- /dev/null
+++ b/engines/composer/resource.cpp
@@ -0,0 +1,337 @@
+/* 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<uint32> Archive::getResourceTypeList() const {
+ Common::Array<uint32> typeList;
+
+ for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++)
+ typeList.push_back(it->_key);
+
+ return typeList;
+}
+
+Common::Array<uint16> Archive::getResourceIDList(uint32 type) const {
+ Common::Array<uint16> 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) {
+ _offset = 0;
+ _stream = stream;
+ _anim = NULL;
+
+ nextFrame();
+}
+
+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);
+ 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);
+ return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
+}
+
+} // End of namespace Composer