[Scummvm-git-logs] scummvm master -> 1fdee2957a6dcb59a8fbd7e851a08c2c08b6bac3

neuromancer noreply at scummvm.org
Tue Nov 18 22:16:13 UTC 2025


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

Summary:
1fdee2957a FREESCAPE: initial implementation for loading Virtual Worlds compressed data for Atari ST (driller)


Commit: 1fdee2957a6dcb59a8fbd7e851a08c2c08b6bac3
    https://github.com/scummvm/scummvm/commit/1fdee2957a6dcb59a8fbd7e851a08c2c08b6bac3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2025-11-18T23:13:41+01:00

Commit Message:
FREESCAPE: initial implementation for loading Virtual Worlds compressed data for Atari ST (driller)

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/games/driller/atari.cpp
    engines/freescape/games/driller/driller.h


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index df1fb850cb8..0f73184a86b 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -161,7 +161,7 @@ static const ADGameDescription gameDescriptions[] = {
 	{
 		// Virtual Worlds release
 		"driller",
-		"This release requires unpacking, check the wiki for instructions: https://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
+		"",
 		{
 			{"d.pak", 0, "607b44b9d31e0da5668b653e03d25efe", 706},
 			{"dril.all", 0, "65277222effa1eb4d73b234245001d75", 158158},
@@ -169,7 +169,7 @@ static const ADGameDescription gameDescriptions[] = {
 		},
 		Common::EN_ANY,
 		Common::kPlatformAtariST,
-		ADGF_UNSUPPORTED,
+		ADGF_UNSTABLE | GF_ATARI_BUDGET,
 		GUIO3(GUIO_NOMIDI, GUIO_RENDERATARIST, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
 	{
diff --git a/engines/freescape/games/driller/atari.cpp b/engines/freescape/games/driller/atari.cpp
index 188898b9c0d..90af8fbeff0 100644
--- a/engines/freescape/games/driller/atari.cpp
+++ b/engines/freescape/games/driller/atari.cpp
@@ -20,6 +20,7 @@
  */
 #include "common/file.h"
 #include "common/memstream.h"
+#include "common/endian.h"
 
 #include "freescape/freescape.h"
 #include "freescape/games/driller/driller.h"
@@ -27,6 +28,149 @@
 
 namespace Freescape {
 
+Common::SeekableReadStream *DrillerEngine::decryptFileAtariVirtualWorlds(const Common::Path &filename) {
+	Common::File file;
+	if (!file.open(filename)) {
+		error("Failed to open %s", filename.toString().c_str());
+	}
+	const int size = file.size();
+	byte *data = (byte *)malloc(size);
+	file.read(data, size);
+
+	int start = 0;
+	int valid_offset = -1;
+	int chunk_size = 0;
+
+	while (true) {
+		byte *found = (byte *)memmem(data + start, size - start, "CBCP", 4);
+		if (!found) break;
+
+		int idx = found - data;
+		if (idx + 8 <= size) {
+			int sz = READ_BE_UINT32(data + idx + 4);
+			if (sz > 0 && sz < size + 0x20000) {
+				valid_offset = idx;
+				chunk_size = sz;
+			}
+		}
+		start = idx + 1;
+	}
+
+	if (valid_offset == -1) {
+		error("No valid CBCP chunk found in %s", filename.toString().c_str());
+	}
+
+	const byte *payload = data + valid_offset + 8;
+	const int payload_size = chunk_size;
+
+	if (payload_size < 12) {
+		error("Payload too short in %s", filename.toString().c_str());
+	}
+
+	uint32 bit_buf_init = READ_BE_UINT32(payload + payload_size - 12);
+	uint32 checksum_init = READ_BE_UINT32(payload + payload_size - 8);
+	uint32 decoded_size = READ_BE_UINT32(payload + payload_size - 4);
+
+	byte *out_buffer = (byte *)malloc(decoded_size);
+	int dst_idx = decoded_size;
+
+	struct BitStream {
+		const byte *_src_data;
+		int _src_idx;
+		uint32 _bit_buffer;
+		uint32 _checksum;
+		int _refill_carry;
+
+		BitStream(const byte *src_data, int start_idx, uint32 bit_buffer, uint32 checksum) :
+			_src_data(src_data), _src_idx(start_idx), _bit_buffer(bit_buffer), _checksum(checksum), _refill_carry(0) {}
+
+		void refill() {
+			if (_src_idx < 0) {
+				_refill_carry = 0;
+				_bit_buffer = 0x80000000;
+				return;
+			}
+			uint32 val = READ_BE_UINT32(_src_data + _src_idx);
+			_src_idx -= 4;
+			_checksum ^= val;
+			_refill_carry = val & 1;
+			_bit_buffer = (val >> 1) | 0x80000000;
+		}
+
+		int getBits(int count) {
+			uint32 result = 0;
+			for (int i = 0; i < count; ++i) {
+				int carry = _bit_buffer & 1;
+				_bit_buffer >>= 1;
+				if (_bit_buffer == 0) {
+					refill();
+					carry = _refill_carry;
+				}
+				result = (result << 1) | carry;
+			}
+			return result;
+		}
+	};
+
+	int src_idx = payload_size - 16;
+	uint32 checksum = checksum_init ^ bit_buf_init;
+	BitStream bs(payload, src_idx, bit_buf_init, checksum);
+
+	while (dst_idx > 0) {
+		if (bs.getBits(1) == 0) {
+			if (bs.getBits(1) == 1) {
+				int offset = bs.getBits(8);
+				for (int i = 0; i < 2; ++i) {
+					dst_idx--;
+					if (dst_idx >= 0) {
+						out_buffer[dst_idx] = out_buffer[dst_idx + offset];
+					}
+				}
+			} else {
+				int count = bs.getBits(3) + 1;
+				for (int i = 0; i < count; ++i) {
+					dst_idx--;
+					if (dst_idx >= 0) {
+						out_buffer[dst_idx] = bs.getBits(8);
+					}
+				}
+			}
+		} else {
+			int tag = bs.getBits(2);
+			if (tag == 3) {
+				int count = bs.getBits(8) + 9;
+				for (int i = 0; i < count; ++i) {
+					dst_idx--;
+					if (dst_idx >= 0) {
+						out_buffer[dst_idx] = bs.getBits(8);
+					}
+				}
+			} else if (tag == 2) {
+				int length = bs.getBits(8) + 1;
+				int offset = bs.getBits(12);
+				for (int i = 0; i < length; ++i) {
+					dst_idx--;
+					if (dst_idx >= 0) {
+						out_buffer[dst_idx] = out_buffer[dst_idx + offset];
+					}
+				}
+			} else {
+				int bits_offset = 9 + tag;
+				int length = 3 + tag;
+				int offset = bs.getBits(bits_offset);
+				for (int i = 0; i < length; ++i) {
+					dst_idx--;
+					if (dst_idx >= 0) {
+						out_buffer[dst_idx] = out_buffer[dst_idx + offset];
+					}
+				}
+			}
+		}
+	}
+	free(data);
+	return new Common::MemoryReadStream(out_buffer, decoded_size);
+}
+
 Common::SeekableReadStream *DrillerEngine::decryptFileAtari(const Common::Path &filename) {
 	Common::File file;
 	file.open(filename);
@@ -61,9 +205,9 @@ Common::SeekableReadStream *DrillerEngine::decryptFileAtari(const Common::Path &
 }
 
 void DrillerEngine::loadAssetsAtariFullGame() {
-
+	Common::SeekableReadStream *stream = nullptr;
 	if (_variant & GF_ATARI_RETAIL) {
-		Common::SeekableReadStream *stream = decryptFileAtari("x.prg");
+		stream = decryptFileAtari("x.prg");
 
 		_border = loadAndConvertNeoImage(stream, 0x14b96);
 		_borderExtra = loadAndConvertNeoImage(stream, 0x1c916);
@@ -84,8 +228,10 @@ void DrillerEngine::loadAssetsAtariFullGame() {
 		Common::File file;
 		file.open("x.prg");
 
-		if (!file.isOpen())
-			error("Failed to open 'x.prg' executable for AtariST");
+		if (!file.isOpen()) {
+			stream = decryptFileAtariVirtualWorlds("dril.all");
+		} else
+			stream = &file;
 
 		if (isSpaceStationOblivion()) {
 			_border = loadAndConvertNeoImage(&file, 0x13544);
@@ -104,23 +250,24 @@ void DrillerEngine::loadAssetsAtariFullGame() {
 			loadPalettes(&file, 0x296fa - 0x1d6);
 			loadSoundsFx(&file, 0x30da6 - 0x1d6, 25);
 		} else {
-			_border = loadAndConvertNeoImage(&file, 0x1371a);
-			_title = loadAndConvertNeoImage(&file, 0x396);
+			_border = loadAndConvertNeoImage(stream, 0x1371a);
+			_title = loadAndConvertNeoImage(stream, 0x396);
 
-			loadFonts(&file, 0x8a32);
+			loadFonts(stream, 0x8a32);
 
 			Common::Array<Graphics::ManagedSurface *> chars;
-			chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, &file, 0x8a32 + 112 * 33 + 1, 100);
+			chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, stream, 0x8a32 + 112 * 33 + 1, 100);
 			_fontSmall = Font(chars);
 			_fontSmall.setCharWidth(5);
 
-			loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
-			loadGlobalObjects(&file, 0xbccc, 8);
-			load8bitBinary(&file, 0x29b3c, 16);
-			loadPalettes(&file, 0x296fa);
-			loadSoundsFx(&file, 0x30da6, 25);
+			loadMessagesFixedSize(stream, 0xc5d8, 14, 20);
+			loadGlobalObjects(stream, 0xbccc, 8);
+			load8bitBinary(stream, 0x29b3c, 16);
+			loadPalettes(stream, 0x296fa);
+			loadSoundsFx(stream, 0x30da6, 25);
 		}
-	}
+	} else
+		error("Unknown Atari ST Driller variant");
 
 	for (auto &area : _areaMap) {
 		// Center and pad each area name so we do not have to do it at each frame
diff --git a/engines/freescape/games/driller/driller.h b/engines/freescape/games/driller/driller.h
index e39c7b53492..5d0ab47deef 100644
--- a/engines/freescape/games/driller/driller.h
+++ b/engines/freescape/games/driller/driller.h
@@ -124,6 +124,7 @@ private:
 	Texture *_borderExtraTexture;
 
 	Common::SeekableReadStream *decryptFileAtari(const Common::Path &filename);
+	Common::SeekableReadStream *decryptFileAtariVirtualWorlds(const Common::Path &filename);
 };
 
 enum DrillerReleaseFlags {




More information about the Scummvm-git-logs mailing list