aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/resource.cpp
diff options
context:
space:
mode:
authorMatthew Hoops2011-01-31 22:45:51 +0000
committerMatthew Hoops2011-01-31 22:45:51 +0000
commit4c8a9ab24ae17d34f9e7323d7b83c77b8ac68ece (patch)
treea1b8f0c18ead60f0eacdf26e4e01ef5fae641c3b /engines/sci/resource.cpp
parent5c1e21818ae85a97dbf0bb10054af6fca341ccfb (diff)
downloadscummvm-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/sci/resource.cpp')
-rw-r--r--engines/sci/resource.cpp120
1 files changed, 112 insertions, 8 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;
}