diff options
author | Matthew Hoops | 2011-01-31 22:45:51 +0000 |
---|---|---|
committer | Matthew Hoops | 2011-01-31 22:45:51 +0000 |
commit | 4c8a9ab24ae17d34f9e7323d7b83c77b8ac68ece (patch) | |
tree | a1b8f0c18ead60f0eacdf26e4e01ef5fae641c3b /engines | |
parent | 5c1e21818ae85a97dbf0bb10054af6fca341ccfb (diff) | |
download | scummvm-rg350-4c8a9ab24ae17d34f9e7323d7b83c77b8ac68ece.tar.gz scummvm-rg350-4c8a9ab24ae17d34f9e7323d7b83c77b8ac68ece.tar.bz2 scummvm-rg350-4c8a9ab24ae17d34f9e7323d7b83c77b8ac68ece.zip |
SCI: Add support for Mac SCI1.1+ resource compression
Mac SCI1.1+ games should now start up. QFG1 and Hoyle4 are playable. GK1 starts its scripts, but errors out soon after. There are still some View bugs with each (somehow, view decompression seems to be adding a blank line after each line?).
*Much* thanks to Walter for his help.
svn-id: r55696
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sci/resource.cpp | 120 | ||||
-rw-r--r-- | engines/sci/resource_intern.h | 9 |
2 files changed, 118 insertions, 11 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 60251ae54b..3362259402 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -393,14 +393,120 @@ void MacResourceForkResourceSource::loadResource(ResourceManager *resMan, Resour if (!stream) error("Could not get Mac resource fork resource: %s %d", getResourceTypeName(res->getType()), res->getNumber()); - int error = res->decompress(resMan->getVolVersion(), stream); - if (error) { - warning("Error %d occurred while reading %s from Mac resource file: %s", - error, res->_id.toString().c_str(), sci_error_types[error]); - res->unalloc(); + decompressResource(stream, res); +} + +bool MacResourceForkResourceSource::isCompressableResource(ResourceType type) const { + // Any types that were not originally an SCI format are not compressed, it seems. + // (Audio being Mac snd resources here) + return type != kResourceTypeMacPict && type != kResourceTypeAudio && + type != kResourceTypeMacIconBarPictN && type != kResourceTypeMacIconBarPictS; +} + +#define OUTPUT_LITERAL() \ + while (literalLength--) \ + *ptr++ = stream->readByte(); + +#define OUTPUT_COPY() \ + while (copyLength--) { \ + byte value = ptr[-offset]; \ + *ptr++ = value; \ + } + +void MacResourceForkResourceSource::decompressResource(Common::SeekableReadStream *stream, Resource *resource) const { + // KQ6 Mac is the only game not compressed. It's not worth writing a + // heuristic just for that game. Also, skip over any resource that cannot + // be compressed. + bool canBeCompressed = !(g_sci && g_sci->getGameId() == GID_KQ6) && isCompressableResource(resource->_id.getType()); + uint32 uncompressedSize = 0; + + // Get the uncompressed size from the end of the resource + if (canBeCompressed && stream->size() > 4) { + stream->seek(stream->size() - 4); + uncompressedSize = stream->readUint32BE(); + stream->seek(0); + } + + if (uncompressedSize == 0) { + // Not compressed + resource->size = stream->size(); + + // Cut out the 'non-compressed marker' (four zeroes) at the end + if (canBeCompressed) + resource->size -= 4; + + resource->data = new byte[resource->size]; + stream->read(resource->data, resource->size); + } else { + // Decompress + resource->size = uncompressedSize; + resource->data = new byte[uncompressedSize]; + + byte *ptr = resource->data; + + while (stream->pos() < stream->size()) { + byte code = stream->readByte(); + + int literalLength = 0, offset = 0, copyLength = 0; + byte extraByte1 = 0, extraByte2 = 0; + + if (code == 0xFF) { + // End of stream marker + break; + } + + switch (code & 0xC0) { + case 0x80: + // Copy chunk expanded + extraByte1 = stream->readByte(); + extraByte2 = stream->readByte(); + + literalLength = extraByte2 & 3; + + OUTPUT_LITERAL() + + offset = ((code & 0x3f) | ((extraByte1 & 0xe0) << 1) | ((extraByte2 & 0xfc) << 7)) + 1; + copyLength = (extraByte1 & 0x1f) + 3; + + OUTPUT_COPY() + break; + case 0xC0: + // Literal chunk + if (code >= 0xD0) { + // These codes cannot be used + if (code == 0xD0 || code > 0xD3) + error("Bad Mac compression code %02x", code); + + literalLength = code & 3; + } else + literalLength = (code & 0xf) * 4 + 4; + + OUTPUT_LITERAL() + break; + default: + // Copy chunk + extraByte1 = stream->readByte(); + + literalLength = (extraByte1 >> 3) & 0x3; + + OUTPUT_LITERAL() + + offset = (code + ((extraByte1 & 0xE0) << 2)) + 1; + copyLength = (extraByte1 & 0x7) + 3; + + OUTPUT_COPY() + break; + } + } } + + resource->_status = kResStatusAllocated; + delete stream; } +#undef OUTPUT_LITERAL +#undef OUTPUT_COPY + Common::SeekableReadStream *ResourceSource::getVolumeFile(ResourceManager *resMan, Resource *res) { Common::SeekableReadStream *fileStream = resMan->getVolumeFile(this); @@ -2082,10 +2188,8 @@ void ResourceManager::detectSciVersion() { // TODO: Decide between SCI2 and SCI2.1 if (Common::File::exists("resource.cfg")) s_sciVersion = SCI_VERSION_1_1; - else if (Common::File::exists("Patches")) - s_sciVersion = SCI_VERSION_2_1; else - s_sciVersion = SCI_VERSION_2; + s_sciVersion = SCI_VERSION_2_1; return; } diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h index 14f872b46e..f7b04b644a 100644 --- a/engines/sci/resource_intern.h +++ b/engines/sci/resource_intern.h @@ -177,9 +177,6 @@ public: * Reads SCI1.1+ resources from a Mac resource fork. */ class MacResourceForkResourceSource : public ResourceSource { -protected: - Common::MacResManager *_macResMan; - public: MacResourceForkResourceSource(const Common::String &name, int volNum); ~MacResourceForkResourceSource(); @@ -187,6 +184,12 @@ public: virtual void scanSource(ResourceManager *resMan); virtual void loadResource(ResourceManager *resMan, Resource *res); + +protected: + Common::MacResManager *_macResMan; + + bool isCompressableResource(ResourceType type) const; + void decompressResource(Common::SeekableReadStream *stream, Resource *resource) const; }; #ifdef ENABLE_SCI32 |