[Scummvm-git-logs] scummvm master -> ea2165908490a99f41115badeac211c29a61209c

somaen einarjohants at gmail.com
Sat Feb 6 00:05:07 UTC 2021


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ea21659084 TINSEL: Implement LZSS decompression for Noir


Commit: ea2165908490a99f41115badeac211c29a61209c
    https://github.com/scummvm/scummvm/commit/ea2165908490a99f41115badeac211c29a61209c
Author: Einar Johan Trøan Sømåen (somaen at scummvm.org)
Date: 2021-02-06T01:04:54+01:00

Commit Message:
TINSEL: Implement LZSS decompression for Noir

Changed paths:
    engines/tinsel/noir/lzss.cpp
    engines/tinsel/noir/lzss.h
    engines/tinsel/tinsel.cpp


diff --git a/engines/tinsel/noir/lzss.cpp b/engines/tinsel/noir/lzss.cpp
index adb37cd4b2..ca4488c9a0 100644
--- a/engines/tinsel/noir/lzss.cpp
+++ b/engines/tinsel/noir/lzss.cpp
@@ -26,10 +26,101 @@
 
 namespace Tinsel {
 
-int decompressLZSS(Common::ReadStream &input, byte *output) {
-	error("TODO: Implement decompression");
-	return 0;
+static byte HIGH_BITS(byte byteValue, int numBits) {
+	unsigned int mask = ((1 << numBits) - 1) << (8 - numBits);
+	return (byteValue & mask) >> (8 - numBits);
 }
 
+static byte LOW_BITS(byte byteValue, int numBits) {
+	unsigned int mask = ((1 << numBits) - 1);
+	return byteValue & mask;
 }
 
+int decompressLZSS(Common::SeekableReadStream &input, byte *output) {
+	static const int kDictionarySize = 4096;
+	byte dictionary[kDictionarySize] = {};
+	int dictionaryOffset = 1;
+	int outputOffset = 0;
+
+	byte *data = new byte[input.size()];
+	input.read(data, input.size());
+	unsigned int offset = 0;
+
+	int bitShift = 0;
+	int bytesWritten = 0;
+
+	while (true) {
+		byte value = data[offset];
+		byte bitMask = 0x80 >> bitShift++;
+		// First bit:
+		// 0 -> Copy data from dictionary
+		// 1 -> Copy raw byte from input
+		bool useRawByte = value & bitMask;
+		if (bitShift == 8) {
+			bitShift = 0;
+			offset++;
+		}
+		if (!useRawByte) {
+			unsigned int bitsFromFirst = 8 - bitShift;
+			unsigned int bitsFromLast = 16 - 8 - bitsFromFirst;
+
+			// The dictionary lookup is 16 bit:
+			// 12 bits for the offset
+			// 4 bits for the run-length
+
+			// Combined with the first bit this makes for 17 bits,
+			// So we will be reading from three bytes, except when
+			// the first bit was read from the end of a byte, then
+			// bitShift will be 0, and bitsFromLast will be 8.
+
+			// We make the assumption that we can dereference the third byte
+			// even if we aren't using it. We will check "offset" after decompression
+			// to verify this assumption.
+			unsigned int byte1 = LOW_BITS(data[offset], bitsFromFirst);
+			unsigned int byte2 = data[offset + 1];
+			unsigned int byte3 = HIGH_BITS(data[offset + 2], bitsFromLast);
+
+			unsigned int lookup = (byte1 << (8 + bitsFromLast)) | (byte2 << bitsFromLast) | byte3;
+
+			int lookupOffset = (lookup >> 4) & 0xFFF;
+			if (lookupOffset == 0) {
+				break;
+			}
+			int lookupRunLength = (lookup & 0xF) + 2;
+			for (int j = 0; j < lookupRunLength; j++) {
+				output[outputOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
+				dictionary[dictionaryOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
+				dictionaryOffset %= kDictionarySize;
+			}
+
+			offset += 2;
+			bytesWritten += lookupRunLength;
+		} else {
+			// Raw byte, but since we spent a bit first,
+			// we must reassemble it from potentially two bytes.
+			unsigned int bitsFromFirst = 8 - bitShift;
+			unsigned int bitsFromLast = 8 - bitsFromFirst;
+
+			byte byteValue = LOW_BITS(data[offset], bitsFromFirst) << bitsFromLast;
+			byteValue |= HIGH_BITS(data[offset + 1], bitsFromLast);
+
+			offset++;
+
+			output[outputOffset++] = byteValue;
+			dictionary[dictionaryOffset++] = byteValue;
+			dictionaryOffset %= kDictionarySize;
+
+			bytesWritten++;
+		}
+
+	}
+	delete[] data;
+
+	if (offset > input.size()) {
+		error("Read too far during decompression");
+	}
+
+	return bytesWritten;
+}
+
+}
diff --git a/engines/tinsel/noir/lzss.h b/engines/tinsel/noir/lzss.h
index d311601cd6..e7735f3cfe 100644
--- a/engines/tinsel/noir/lzss.h
+++ b/engines/tinsel/noir/lzss.h
@@ -28,7 +28,7 @@
 
 namespace Tinsel {
 
-int decompressLZSS(Common::ReadStream &input, byte *output);
+int decompressLZSS(Common::SeekableReadStream &input, byte *output);
 
 }
 
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index ed4a231a38..53d2eb7110 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -725,6 +725,10 @@ void LoadBasicChunks() {
 	byte *cptr;
 	int numObjects;
 
+	if (TinselV3) {
+		error("TODO: Implement LoadBasicChunks for Noir");
+	}
+
 	// Allocate RAM for savescene data
 	InitializeSaveScenes();
 




More information about the Scummvm-git-logs mailing list