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

sdelamarre noreply at scummvm.org
Tue Aug 12 15:31:29 UTC 2025


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

Summary:
1f1b3c7e1e GOB: Use same "if" branching as Adibou2 for Adi4 where relevant
615a286a28 GOB: Fix a crash when trying to convert invalid VMD frames to high-color (Adi4)
502fe84264 GOB: Implement an unpackChunk() extension used in some games
e32971ecb7 GOB: Handle high-color packed sprites in Adi4
d77c08b936 GOB: Add some Adi4 save files
0f12e0c233 GOB: Add Adi4 application directories to the search path
18de0ea90c GOB: Add o7_xorObfuscate and o7_xorDeobfuscate opcodes (Adi4)
82d3bb2d49 GOB: Add a missing dummy opcode used in Adi4
cd52af636d GOB: Reorganize Adi4 add-ons by age ranges


Commit: 1f1b3c7e1e9eb58111ae22632c022f35014fe06e
    https://github.com/scummvm/scummvm/commit/1f1b3c7e1e9eb58111ae22632c022f35014fe06e
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Use same "if" branching as Adibou2 for Adi4 where relevant

Changed paths:
    engines/gob/game.cpp
    engines/gob/hotspots.cpp
    engines/gob/inter_v1.cpp
    engines/gob/mult_v2.cpp
    engines/gob/resources.cpp
    engines/gob/scenery.cpp
    engines/gob/sound/sound.cpp
    engines/gob/util.cpp
    engines/gob/video.cpp
    engines/gob/videoplayer.cpp


diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp
index ea1cd8d72ed..d6eb63ce2e2 100644
--- a/engines/gob/game.cpp
+++ b/engines/gob/game.cpp
@@ -835,7 +835,7 @@ void Game::evaluateScroll() {
 int16 Game::checkKeys(int16 *pMouseX, int16 *pMouseY,
 		MouseButtons *pButtons, char handleMouse) {
 
-	if (_vm->getGameType() == kGameTypeAdibou2)
+	if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
 		_vm->_vidPlayer->updateLive();
 	_vm->_util->processInput(true);
 
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index 5d9547a558a..d71b57ea158 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -548,7 +548,7 @@ void Hotspots::leave(uint16 index) {
 
 	if (spot.funcLeave != 0) {
 		Script *curScript = _vm->_game->_script;
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			_vm->_game->_script = spot.scriptFuncLeave;
 		}
 		call(spot.funcLeave);
@@ -737,7 +737,7 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
 		_currentIndex = 0;
 	}
 
-	if (_vm->getGameType() == kGameTypeAdibou2 &&
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) &&
 			_currentKey != 0 &&
 			(_hotspots[_currentIndex].id != _currentId ||
 			_hotspots[_currentIndex].key != _currentKey)) {
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index ec5f96b3da5..fdf46fcb197 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -1292,7 +1292,7 @@ void Inter_v1::o1_palLoad(OpFuncParams &params) {
 			_vm->_draw->_vgaPalette[0].blue  = 0;
 		}
 
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			_vm->_draw->_vgaPalette[0].red = 0;
 			_vm->_draw->_vgaPalette[0].green = 0;
 			_vm->_draw->_vgaPalette[0].blue = 0;
@@ -1313,7 +1313,7 @@ void Inter_v1::o1_palLoad(OpFuncParams &params) {
 	}
 
 	if (!_vm->_draw->_applyPal) {
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			if (_vm->_global->_pPaletteDesc)
 				_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
 			else
@@ -1372,11 +1372,13 @@ void Inter_v1::o1_keyFunc(OpFuncParams &params) {
 		break;
 
 	case -1:
-		if (_vm->getGameType() != kGameTypeAdibou2)
+		if (_vm->getGameType() != kGameTypeAdibou2 && _vm->getGameType() != kGameTypeAdi4)
 			break;
 		// fall through
 	case 1:
-		if (_vm->getGameType() != kGameTypeFascination && _vm->getGameType() != kGameTypeAdibou2)
+		if (_vm->getGameType() != kGameTypeFascination &&
+				_vm->getGameType() != kGameTypeAdibou2 &&
+				_vm->getGameType() != kGameTypeAdi4)
 			_vm->_util->forceMouseUp(true);
 
 		// FIXME This is a hack to fix an issue with "text" tool in Adibou2 paint game.
@@ -1385,11 +1387,9 @@ void Inter_v1::o1_keyFunc(OpFuncParams &params) {
 		// the key buffer, and the loop continues.
 		// Strangely in the original game it seems that the event is always caught by the
 		// second keyFunc.
-		if (_vm->getGameType() == kGameTypeAdibou2
-			&&
-			(_vm->_game->_script->pos() == 18750 || _vm->_game->_script->pos() == 18955)
-			&&
-			_vm->isCurrentTot("palette.tot"))
+		if (_vm->getGameType() == kGameTypeAdibou2 &&
+				(_vm->_game->_script->pos() == 18750 || _vm->_game->_script->pos() == 18955) &&
+				_vm->isCurrentTot("palette.tot"))
 			break;
 
 		key = _vm->_game->checkKeys(&_vm->_global->_inter_mouseX,
@@ -1411,7 +1411,7 @@ void Inter_v1::o1_keyFunc(OpFuncParams &params) {
 			_vm->_util->delay(cmd);
 			_noBusyWait = true;
 		} else {
-			if (_vm->getGameType() == kGameTypeAdibou2) {
+			if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 				// The engine calls updateLive() every 100ms while waiting there
 				while (cmd > 100) {
 					_vm->_vidPlayer->updateLive();
diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp
index ed453d819a3..22bc5cf2534 100644
--- a/engines/gob/mult_v2.cpp
+++ b/engines/gob/mult_v2.cpp
@@ -705,7 +705,7 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
 		animLayer = _vm->_scenery->getAnimLayer(nAnim, nLayer);
 	} else {
 		if (animObj.videoSlot > 0) {
-			if (_vm->getGameType() == kGameTypeAdibou2) {
+			if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 				int expectedFrame = _vm->_vidPlayer->getExpectedFrameFromCurrentTime(animObj.videoSlot - 1);
 				if (expectedFrame >= 0 &&
 					expectedFrame < animData.frame) {
@@ -752,7 +752,7 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
 
 	if (animData.animType != 8) {
 		animData.frame++;
-		if (_vm->getGameType() == kGameTypeAdibou2
+		if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
 			&&
 			animData.animation < 0
 			&&
diff --git a/engines/gob/resources.cpp b/engines/gob/resources.cpp
index f356e948131..d6543474e4f 100644
--- a/engines/gob/resources.cpp
+++ b/engines/gob/resources.cpp
@@ -177,7 +177,7 @@ bool Resources::load(const Common::String &fileName) {
 	}
 
 	if (!hasTOTRes && !hasEXTRes) {
-		if (_vm->getGameType() == kGameTypeAdibou2)
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
 			return true; // Some "library" TOT files used in Adibou2 have no embed resources, nor external ones.
 		else
 			return false;
@@ -521,7 +521,7 @@ byte *Resources::loadTOTLocTexts(const Common::String &fileBase, int32 &size) {
 		}
 
 		// The .ALL suffix, normally for German, is also used for Italian in Adibou2
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			if (_vm->_global->_languageWanted == kLanguageItalian) {
 				locTextFile = getLocTextFile(fileBase, kLanguageGerman);
 				if (!locTextFile.empty())
diff --git a/engines/gob/scenery.cpp b/engines/gob/scenery.cpp
index 827fcfeb93d..0d6e52b19f1 100644
--- a/engines/gob/scenery.cpp
+++ b/engines/gob/scenery.cpp
@@ -651,7 +651,7 @@ void Scenery::updateAnimObjectVideo(int16 layer, int16 frame, int16 animation, i
 	Mult::Mult_Object &obj = _vm->_mult->_objects[-animation - 1];
 
 	if ((obj.videoSlot == 0) || !_vm->_vidPlayer->slotIsOpen(obj.videoSlot - 1)) {
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			if (!(flags & 4))
 				_toRedrawLeft = -12345;
 
@@ -1052,9 +1052,10 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
 	int16 destX;
 	int16 destY;
 
-	if ((animation < 0) &&
-	    ((_vm->getGameType() == kGameTypeWoodruff) ||
-	     (_vm->getGameType() == kGameTypeAdibou2))) {
+	if (animation < 0 &&
+	    (_vm->getGameType() == kGameTypeWoodruff ||
+	     _vm->getGameType() == kGameTypeAdibou2 ||
+	     _vm->getGameType() == kGameTypeAdi4)) {
 		// Object video
 
 		updateAnimObjectVideo(layer, frame, animation, flags, drawDeltaX, drawDeltaY, doDraw);
@@ -1062,7 +1063,7 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
 	}
 
 
-	if ((_vm->getGameType() == kGameTypeAdibou2) && animation >= 0) {
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) && animation >= 0) {
 		_toRedrawRight = 1000;
 		_toRedrawBottom = 1000;
 		_toRedrawLeft = 1000;
diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp
index 3d3e5bdfe38..c2e180766f1 100644
--- a/engines/gob/sound/sound.cpp
+++ b/engines/gob/sound/sound.cpp
@@ -67,8 +67,9 @@ Sound::Sound(GobEngine *vm) : _vm(vm) {
 		_cdrom = new CDROM;
 	if (_vm->getGameType() == kGameTypeWoodruff)
 		_bgatmos = new BackgroundAtmosphere(*_vm->_mixer);
-	if ((_vm->getGameType() == kGameTypeUrban) ||
-	    (_vm->getGameType() == kGameTypeAdibou2)) {
+	if (_vm->getGameType() == kGameTypeUrban ||
+	    _vm->getGameType() == kGameTypeAdibou2 ||
+	    _vm->getGameType() == kGameTypeAdi4) {
 		_bgatmos = new BackgroundAtmosphere(*_vm->_mixer);
 		_bgatmos->setShadable(false);
 	}
diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp
index 875270b30ea..edd8fc005cb 100644
--- a/engines/gob/util.cpp
+++ b/engines/gob/util.cpp
@@ -100,7 +100,7 @@ void Util::processInput(bool scroll) {
 	int16 x = 0, y = 0;
 	bool hasMove = false;
 
-	if (_vm->getGameType() != kGameTypeAdibou2)
+	if (_vm->getGameType() != kGameTypeAdibou2 && _vm->getGameType() != kGameTypeAdi4)
 		_vm->_vidPlayer->updateLive();
 
 	while (eventMan->pollEvent(event)) {
diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp
index a8c396eafc1..9c179016fe2 100644
--- a/engines/gob/video.cpp
+++ b/engines/gob/video.cpp
@@ -245,7 +245,9 @@ SurfacePtr Video::initSurfDesc(int16 width, int16 height, int16 flags, byte bpp)
 		assert(!(flags & DISABLE_SPR_ALLOC));
 
 
-		if (!(flags & SCUMMVM_CURSOR) && _vm->getGameType() != kGameTypeAdibou2)
+		if (!(flags & SCUMMVM_CURSOR) &&
+				_vm->getGameType() != kGameTypeAdibou2 &&
+				_vm->getGameType() != kGameTypeAdi4)
 			width = (width + 7) & 0xFFF8;
 
 		descPtr = SurfacePtr(new Surface(width,
@@ -403,12 +405,16 @@ void Video::setPalElem(int16 index, char red, char green, char blue,
 
 	if (_vm->getPixelFormat().bytesPerPixel == 1)
 		g_system->getPaletteManager()->setPalette(pal, index, 1);
-	else
+	else {
+		bool useSpecialBlackWhiteValues = _vm->getGameType() == kGameTypeAdibou2 ||
+										  _vm->getGameType() == kGameTypeAdi4;
+
 		Surface::computeHighColorMap(_vm->_global->_pPaletteDesc->highColorMap,
 									 pal,
 									 _vm->getPixelFormat(),
-									 _vm->getGameType() == kGameTypeAdibou2,
+									 useSpecialBlackWhiteValues,
 									 index, 1, 0);
+	}
 }
 
 void Video::setPalette(PalDesc *palDesc) {
@@ -423,12 +429,16 @@ void Video::setPalette(PalDesc *palDesc) {
 
 	if (_vm->getPixelFormat().bytesPerPixel == 1)
 		g_system->getPaletteManager()->setPalette(pal, 0, numcolors);
-	else
+	else {
+		bool useSpecialBlackWhiteValues = _vm->getGameType() == kGameTypeAdibou2 ||
+										  _vm->getGameType() == kGameTypeAdi4;
+
 		Surface::computeHighColorMap(palDesc->highColorMap,
 									 pal,
 									 _vm->getPixelFormat(),
-									 _vm->getGameType() == kGameTypeAdibou2,
+									 useSpecialBlackWhiteValues,
 									 0, numcolors);
+	}
 }
 
 void Video::setFullPalette(PalDesc *palDesc) {
@@ -445,11 +455,15 @@ void Video::setFullPalette(PalDesc *palDesc) {
 
 		if (_vm->getPixelFormat().bytesPerPixel == 1)
 			g_system->getPaletteManager()->setPalette(pal, 0, 256);
-		else
+		else {
+			bool useSpecialBlackWhiteValues = _vm->getGameType() == kGameTypeAdibou2 ||
+											  _vm->getGameType() == kGameTypeAdi4;
+
 			Surface::computeHighColorMap(palDesc->highColorMap,
 										 pal,
 										 _vm->getPixelFormat(),
-										 _vm->getGameType() == kGameTypeAdibou2);
+										 useSpecialBlackWhiteValues);
+		}
 	} else
 		Video::setPalette(palDesc);
 }
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 757a24bc94e..3a5ef134038 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -160,7 +160,7 @@ int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties
 	if (!video->isEmpty() && (video->fileName.compareToIgnoreCase(file) != 0))
 		video->close();
 
-	if (_vm->getGameType() == kGameTypeAdibou2 &&
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) &&
 		(file.empty() || file.equalsIgnoreCase("RIEN"))) {
 		// An empty filename means that we just need to close any existing video in the slot
 		// "RIEN" (French for "nothing") is an alias for that
@@ -188,6 +188,11 @@ int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties
 		// Set the filename
 		video->fileName = file;
 
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
+			_noCursorSwitch = true; // For Adibou2, we always want to see the cursor while a video is playing.
+		else
+			_noCursorSwitch = false;
+
 		// WORKAROUND: In some rare cases, the cursor should still be
 		// displayed while a video is playing.
 		Common::String videoFile = file;
@@ -195,11 +200,6 @@ int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties
 		if (videoFile.hasSuffix(".IMD"))
 			videoFile = videoFile.substr(0, videoFile.find('.'));
 
-		if (_vm->getGameType() == kGameTypeAdibou2)
-			_noCursorSwitch = true; // For Adibou2, we want to see the cursor, on the contrary
-		else
-			_noCursorSwitch = false;
-
 		if (primary && (_vm->getGameType() == kGameTypeLostInTime)) {
 			static const Common::StringArray videosWithCursorLIT = {
 				"PORTA03", "PORTA03A", "CALE1", "AMIL2", "AMIL3B", "DELB", "DELG"
@@ -386,7 +386,7 @@ void VideoPlayer::waitSoundEnd(int slot) {
 }
 
 bool VideoPlayer::lastFrameReached(Video &video, Properties &properties) {
-	if (_vm->getGameType() == kGameTypeAdibou2) {
+	if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 		return properties.startFrame >= properties.lastFrame;
 	} else {
 		return (properties.startFrame == properties.lastFrame ||
@@ -431,15 +431,17 @@ bool VideoPlayer::play(int slot, Properties &properties) {
 		video->live       = true;
 		video->properties = properties;
 
-		if (_vm->getGameType() != kGameTypeAdibou2) {
+		if (_vm->getGameType() != kGameTypeAdibou2 &&
+				_vm->getGameType() != kGameTypeAdi4) {
 			updateLive(slot, true);
 			return true;
 		}
 	}
 
 	if (_vm->getGameType() != kGameTypeUrban &&
-		_vm->getGameType() != kGameTypeBambou &&
-		_vm->getGameType() != kGameTypeAdibou2)
+			_vm->getGameType() != kGameTypeBambou &&
+			_vm->getGameType() != kGameTypeAdibou2 &&
+			_vm->getGameType() != kGameTypeAdi4)
 		// NOTE: For testing (and comfort?) purposes, we enable aborting of all videos.
 		//       Except for Urban Runner, Bambou and Adibou2 where it leads to glitches
 		properties.breakKey = kShortKeyEscape;
@@ -449,13 +451,15 @@ bool VideoPlayer::play(int slot, Properties &properties) {
 
 	while (!lastFrameReached(*video, properties)) {
 
-		if (_vm->getGameType() == kGameTypeAdibou2 && video->live) {
+		if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) && video->live) {
 			properties.startFrame = video->decoder->getCurFrame() +
 									video->decoder->getNbFramesPastEnd();
 		}
 
 		bool playFrameResult = playFrame(slot, properties);
-		if (_vm->getGameType() == kGameTypeAdibou2 && !playFrameResult && slot < kLiveVideoSlotCount) {
+		if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) &&
+				!playFrameResult &&
+				slot < kLiveVideoSlotCount) {
 			_vm->_util->processInput();
 			_vm->_video->retrace();
 			_vm->_util->delay(5);
@@ -465,7 +469,7 @@ bool VideoPlayer::play(int slot, Properties &properties) {
 			if (properties.canceled)
 			break;
 
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			WRITE_VAR(11, properties.startFrame + 1);
 			if (slot >= 0 && slot < kVideoSlotWithCurFrameVarCount)
 				WRITE_VAR(53 + slot, properties.startFrame + 1);
@@ -526,12 +530,13 @@ bool VideoPlayer::isSoundPlaying() const {
 }
 
 void VideoPlayer::updateLive(bool force, int exceptSlot) {
-	int nbrOfSlots = (_vm->getGameType() == kGameTypeAdibou2) ?
+	int nbrOfSlots = (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) ?
 					 kLiveVideoSlotCount : kVideoSlotCount;
 
 	for (int i = 0; i < nbrOfSlots; i++) {
-		if (_vm->getGameType() == kGameTypeAdibou2 &&
-			i >= 0 && i < kVideoSlotWithCurFrameVarCount)
+		if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) &&
+				i >= 0 &&
+				i < kVideoSlotWithCurFrameVarCount)
 			WRITE_VAR(53 + i, -1);
 
 		if (i != exceptSlot)
@@ -544,7 +549,8 @@ void VideoPlayer::updateLive(int slot, bool force) {
 	if (!video || !video->live)
 		return;
 
-	if (_vm->getGameType() == kGameTypeAdibou2 && slot < kVideoSlotWithCurFrameVarCount)
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
+			&& slot < kVideoSlotWithCurFrameVarCount)
 		WRITE_VAR(53 + slot, video->decoder->getCurFrame() + video->decoder->getNbFramesPastEnd());
 
 	int nbrOfLiveVideos = 0;
@@ -554,7 +560,7 @@ void VideoPlayer::updateLive(int slot, bool force) {
 			++nbrOfLiveVideos;
 	}
 
-	if (_vm->getGameType() == kGameTypeAdibou2) {
+	if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 		if (video->decoder->hasVideo() &&
 			!video->properties.noWaitSound)
 			return;
@@ -566,7 +572,7 @@ void VideoPlayer::updateLive(int slot, bool force) {
 		// Video ended
 
 		if (!video->properties.loop) {
-			if (_vm->getGameType() != kGameTypeAdibou2) {
+			if (_vm->getGameType() != kGameTypeAdibou2 && _vm->getGameType() != kGameTypeAdi4) {
 				if (!(video->properties.flags & kFlagNoVideo) || nbrOfLiveVideos == 1)
 					WRITE_VAR(53, (uint32)-1);
 			}
@@ -579,27 +585,30 @@ void VideoPlayer::updateLive(int slot, bool force) {
 		}
 	}
 
-	if (video->properties.startFrame == video->properties.lastFrame
-		&& _vm->getGameType() != kGameTypeAdibou2)
+	if (video->properties.startFrame == video->properties.lastFrame &&
+			_vm->getGameType() != kGameTypeAdibou2 &&
+			_vm->getGameType() != kGameTypeAdi4)
 		// Current video sequence ended
 		return;
 
 	if (!force && (video->decoder->getTimeToNextFrame() > 0))
 		return;
 
-	if (_vm->getGameType() != kGameTypeAdibou2  &&
-		(!(video->properties.flags & kFlagNoVideo) || nbrOfLiveVideos == 1))
+	if (_vm->getGameType() != kGameTypeAdibou2 &&
+			_vm->getGameType() != kGameTypeAdi4 &&
+			(!(video->properties.flags & kFlagNoVideo) || nbrOfLiveVideos == 1))
 		WRITE_VAR(53, video->properties.startFrame + 1);
 
 	bool backwards = video->properties.startFrame > video->properties.lastFrame;
 	playFrame(slot, video->properties);
 
-	if (_vm->getGameType() == kGameTypeAdibou2)
+	if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
 		video->properties.startFrame = video->decoder->getCurFrame();
 	else
 		video->properties.startFrame += backwards ? -1 : 1;
 
-	if (_vm->getGameType() == kGameTypeAdibou2 && slot < kVideoSlotWithCurFrameVarCount)
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
+			&& slot < kVideoSlotWithCurFrameVarCount)
 		WRITE_VAR(53 + slot, video->decoder->getCurFrame() + video->decoder->getNbFramesPastEnd());
 
 	if (video->properties.fade) {
@@ -614,11 +623,14 @@ bool VideoPlayer::playFrame(int slot, Properties &properties) {
 		return false;
 
 	bool primary = slot == 0 ||
-				   (_vm->getGameType() == kGameTypeAdibou2 &&
+				   ((_vm->getGameType() == kGameTypeAdibou2 ||
+				   	_vm->getGameType() == kGameTypeAdi4) &&
 					slot < kLiveVideoSlotCount);
 
 	if (video->decoder->getCurFrame() != properties.startFrame) {
-		if (video->live && _vm->getGameType() == kGameTypeAdibou2)
+		if (video->live &&
+				(_vm->getGameType() == kGameTypeAdibou2 ||
+				 _vm->getGameType() == kGameTypeAdi4))
 			return true;
 
 		if (properties.startFrame != -1) {
@@ -648,7 +660,8 @@ bool VideoPlayer::playFrame(int slot, Properties &properties) {
 
 	}
 
-	if (_vm->getGameType() == kGameTypeAdibou2 && video->decoder->getTimeToNextFrame() > 0)
+	if ((_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) &&
+			video->decoder->getTimeToNextFrame() > 0)
 		return false;
 
 	if (video->decoder->getCurFrame() > properties.startFrame)
@@ -693,7 +706,7 @@ bool VideoPlayer::playFrame(int slot, Properties &properties) {
 		}
 	}
 
-	if (_vm->getGameType() != kGameTypeAdibou2)
+	if (_vm->getGameType() != kGameTypeAdibou2 && _vm->getGameType() != kGameTypeAdi4)
 		WRITE_VAR(11, video->decoder->getCurFrame());
 
 	uint32 ignoreBorder = 0;
@@ -757,7 +770,7 @@ bool VideoPlayer::playFrame(int slot, Properties &properties) {
 	}
 
 	bool needCheckAbort = false;
-	if (_vm->getGameType() == kGameTypeAdibou2)
+	if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4)
 		needCheckAbort = properties.breakKey != 0;
 	else
 		needCheckAbort = primary && properties.waitEndFrame;
@@ -791,7 +804,7 @@ void VideoPlayer::checkAbort(Video &video, Properties &properties) {
 
 		// Check for that specific key
 		bool pressedBreak = (VAR(0) == (unsigned)properties.breakKey);
-		if (_vm->getGameType() == kGameTypeAdibou2) {
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
 			if (pressedBreak ||
 				_vm->_game->_mouseButtons == properties.breakKey) {
 				properties.canceled = true;
@@ -1193,10 +1206,13 @@ void VideoPlayer::copyPalette(const Video &video, int16 palStart, int16 palEnd)
 	for (int i = palStart * 3; i < (palEnd + 1) * 3; i++)
 		((char *)(_vm->_global->_pPaletteDesc->vgaPal))[i] = video.decoder->getPalette()[i] >> 2;
 
+	bool useSpecialBlackWhiteValues = _vm->getGameType() == kGameTypeAdibou2 ||
+									  _vm->getGameType() == kGameTypeAdi4;
+
 	Surface::computeHighColorMap(_vm->_global->_pPaletteDesc->highColorMap,
 								 video.decoder->getPalette(),
 								 _vm->getPixelFormat(),
-								 _vm->getGameType() == kGameTypeAdibou2,
+								 useSpecialBlackWhiteValues,
 								 palStart, palEnd - palStart + 1);
 }
 


Commit: 615a286a28fbbabcaea3e7921987557845972f80
    https://github.com/scummvm/scummvm/commit/615a286a28fbbabcaea3e7921987557845972f80
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Fix a crash when trying to convert invalid VMD frames to high-color (Adi4)

Changed paths:
    engines/gob/videoplayer.cpp


diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 3a5ef134038..5b7a57042dd 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -691,7 +691,7 @@ bool VideoPlayer::playFrame(int slot, Properties &properties) {
 	}
 
 	const Graphics::Surface *surface = video->decoder->decodeNextFrame();
-	if (surface != nullptr && video->decoder->isPaletted() && video->surface && video->surface->getBPP() > 1) {
+	if (surface != nullptr && surface->w > 0 && surface->h > 0 && video->decoder->isPaletted() && video->surface && video->surface->getBPP() > 1) {
 		int16 x = 0;
 		int16 y = 0;
 		int16 width = 0;


Commit: 502fe84264b2fd5f375ff87b3a60d996c31f1ca5
    https://github.com/scummvm/scummvm/commit/502fe84264b2fd5f375ff87b3a60d996c31f1ca5
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Implement an unpackChunk() extension used in some games

This extension allows larger strings to be copied from the sliding window when unpacking.

The distinction between the standard and extended format is done by testing the presence of some magic numbers (0x1234 + 0x5678) at the beginning of the file. Those magic numbers have no chance to appear at the beginning of a file using the standard format (it's a valid sequence, but it would encode a very inefficient way to copy some spaces from the initial window).

Changed paths:
    engines/gob/dataio.cpp


diff --git a/engines/gob/dataio.cpp b/engines/gob/dataio.cpp
index 3d58597fc61..5468139332a 100644
--- a/engines/gob/dataio.cpp
+++ b/engines/gob/dataio.cpp
@@ -162,14 +162,30 @@ void DataIO::unpackChunks(Common::SeekableReadStream &src, byte *dest, uint32 si
 }
 
 void DataIO::unpackChunk(Common::SeekableReadStream &src, byte *dest, uint32 size) {
-	byte *tmpBuf = new byte[4114];
+	byte *tmpBuf = new byte[4370]; // 4096 + (256 + 18) = 4096 + (max string length)
 	assert(tmpBuf);
 
 	uint32 counter = size;
 
-	for (int i = 0; i < 4078; i++)
-		tmpBuf[i] = 0x20;
-	uint16 tmpIndex = 4078;
+	uint16 magic1 = src.readUint16LE();
+	uint16 magic2 = src.readUint16LE();
+
+	int16 tmpIndex, extendedLenCmd;
+	if ((magic1 == 0x1234) && (magic2 == 0x5678)) {
+		// Extended format allowing to copy larger strings
+		// from the window (up to 256 + 18 = 274 bytes).
+		extendedLenCmd = 18;
+		tmpIndex = 273;
+	} else {
+		// Standard format allowing to copy short strings
+		// (up to 18 bytes) from the window.
+		extendedLenCmd = 100; // Cannot be matched
+		tmpIndex = 4078;
+		src.seek(-4, SEEK_CUR);
+	}
+
+	memset(tmpBuf, 0x20, tmpIndex); // Fill initial window with spaces
+
 
 	uint16 cmd = 0;
 	while (1) {
@@ -193,7 +209,10 @@ void DataIO::unpackChunk(Common::SeekableReadStream &src, byte *dest, uint32 siz
 			byte tmp2 = src.readByte();
 
 			int16 off = tmp1 | ((tmp2 & 0xF0) << 4);
-			byte  len =         (tmp2 & 0x0F) + 3;
+			int16 len =         (tmp2 & 0x0F) + 3;
+
+			if (len == extendedLenCmd)
+				len = src.readByte() + 18;
 
 			for (int i = 0; i < len; i++) {
 				*dest++ = tmpBuf[(off + i) % 4096];


Commit: e32971ecb7dd13c6777eb4f39fae1853df0fefab
    https://github.com/scummvm/scummvm/commit/e32971ecb7dd13c6777eb4f39fae1853df0fefab
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Handle high-color packed sprites in Adi4

Changed paths:
    engines/gob/cmpfile.cpp
    engines/gob/draw_fascin.cpp
    engines/gob/draw_playtoons.cpp
    engines/gob/draw_v1.cpp
    engines/gob/draw_v2.cpp
    engines/gob/init.cpp
    engines/gob/inter_v1.cpp
    engines/gob/inter_v6.cpp
    engines/gob/inter_v7.cpp
    engines/gob/video.cpp
    engines/gob/video.h
    engines/gob/video_v1.cpp
    engines/gob/video_v2.cpp
    engines/gob/video_v6.cpp


diff --git a/engines/gob/cmpfile.cpp b/engines/gob/cmpfile.cpp
index 183d48ed438..8ef50ebd0c9 100644
--- a/engines/gob/cmpfile.cpp
+++ b/engines/gob/cmpfile.cpp
@@ -146,7 +146,7 @@ void CMPFile::loadCMP(Common::SeekableReadStream &cmp) {
 		return;
 	}
 
-	_vm->_video->drawPackedSprite(data, _surface->getWidth(), _surface->getHeight(), 0, 0, 0, *_surface);
+	_vm->_video->drawPackedSprite(data, size, _surface->getWidth(), _surface->getHeight(), 0, 0, 0, *_surface);
 
 	delete[] data;
 }
diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp
index ddd0d0b8ee0..2f1203ebc9a 100644
--- a/engines/gob/draw_fascin.cpp
+++ b/engines/gob/draw_fascin.cpp
@@ -195,7 +195,7 @@ void Draw_Fascination::spriteOperation(int16 operation) {
 		if (!resource)
 			break;
 
-		_vm->_video->drawPackedSprite(resource->getData(),
+		_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
 				_transparency, *_spritesArray[_destSurface]);
 
@@ -422,7 +422,7 @@ void Draw_Fascination::drawWin(int16 fct) {
 			break;
 		}
 
-		_vm->_video->drawPackedSprite(resource->getData(),
+		_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
 				_transparency, *_spritesArray[_destSurface]);
 
@@ -727,7 +727,7 @@ void Draw_Fascination::decompWin(int16 x, int16 y, SurfacePtr destPtr) {
 	if (!resource)
 		return;
 
-	_vm->_video->drawPackedSprite(resource->getData(),
+	_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 			_spriteRight, _spriteBottom, x, y, _transparency, *destPtr);
 
 	delete resource;
diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 5be9d7d0ccb..ac439173a72 100644
--- a/engines/gob/draw_playtoons.cpp
+++ b/engines/gob/draw_playtoons.cpp
@@ -288,7 +288,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
 		if (!resource)
 			break;
 
-		_vm->_video->drawPackedSprite(resource->getData(),
+		_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
 				_transparency, *_spritesArray[_destSurface]);
 
diff --git a/engines/gob/draw_v1.cpp b/engines/gob/draw_v1.cpp
index 9de61c0e045..1cecff2583e 100644
--- a/engines/gob/draw_v1.cpp
+++ b/engines/gob/draw_v1.cpp
@@ -394,7 +394,7 @@ void Draw_v1::spriteOperation(int16 operation) {
 		if (!resource)
 			break;
 
-		_vm->_video->drawPackedSprite(resource->getData(),
+		_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
 				_transparency, *_spritesArray[_destSurface]);
 
diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp
index 816c6c2d856..9cf8b2d85ea 100644
--- a/engines/gob/draw_v2.cpp
+++ b/engines/gob/draw_v2.cpp
@@ -800,7 +800,7 @@ void Draw_v2::spriteOperation(int16 operation) {
 		if (_vm->_draw->_needAdjust == 3 || _vm->_draw->_needAdjust == 4)
 			adjustCoords(0, &_spriteRight, &_spriteBottom);
 
-		_vm->_video->drawPackedSprite(resource->getData(),
+		_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
 				_transparency, *_spritesArray[_destSurface]);
 
diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp
index cf31c4ed937..885c8460221 100644
--- a/engines/gob/init.cpp
+++ b/engines/gob/init.cpp
@@ -194,7 +194,7 @@ void Init::initGame() {
 				int32 size;
 				byte *sprite = _vm->_dataIO->getFile("coktel.ims", size);
 				if (sprite) {
-					_vm->_video->drawPackedSprite(sprite, 320, 200, 0, 0, 0,
+					_vm->_video->drawPackedSprite(sprite, size, 320, 200, 0, 0, 0,
 							*_vm->_draw->_frontSurface);
 					_vm->_palAnim->fade(_palDesc, 0, 0);
 					_vm->_util->delay(500);
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index fdf46fcb197..5f0578fa4d3 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -772,7 +772,7 @@ void Inter_v1::o1_loadCursor(OpFuncParams &params) {
 	int16 height = resource->getHeight();
 	_vm->_draw->adjustCoords(0, &width, &height);
 
-	_vm->_video->drawPackedSprite(resource->getData(),
+	_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 			width, height,
 			index * _vm->_draw->_cursorWidth, 0, 0, *_vm->_draw->_cursorSprites);
 	_vm->_draw->_cursorAnimLow[index] = 0;
diff --git a/engines/gob/inter_v6.cpp b/engines/gob/inter_v6.cpp
index 9684746ea9b..a62c00bdb3b 100644
--- a/engines/gob/inter_v6.cpp
+++ b/engines/gob/inter_v6.cpp
@@ -252,7 +252,7 @@ void Inter_v6::o6_loadCursor(OpFuncParams &params) {
 			index * _vm->_draw->_cursorWidth + _vm->_draw->_cursorWidth - 1,
 			_vm->_draw->_cursorHeight - 1, 0);
 
-	_vm->_video->drawPackedSprite(resource->getData(),
+	_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 			resource->getWidth(), resource->getHeight(),
 			index * _vm->_draw->_cursorWidth, 0, 0, *_vm->_draw->_cursorSprites);
 	_vm->_draw->_cursorAnimLow[index] = 0;
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index 6775eed2ba3..ea1b67de3a9 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -228,7 +228,7 @@ void Inter_v7::o7_loadCursor(OpFuncParams &params) {
 										 index * _vm->_draw->_cursorWidth + _vm->_draw->_cursorWidth - 1,
 										 _vm->_draw->_cursorHeight - 1, 0);
 
-	_vm->_video->drawPackedSprite(resource->getData(),
+	_vm->_video->drawPackedSprite(resource->getData(), resource->getSize(),
 								  resource->getWidth(), resource->getHeight(),
 								  index * _vm->_draw->_cursorWidth, 0, 0, *_vm->_draw->_cursorSprites);
 	_vm->_draw->_cursorAnimLow[index] = 0;
diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp
index 9c179016fe2..8ad1c8b3655 100644
--- a/engines/gob/video.cpp
+++ b/engines/gob/video.cpp
@@ -325,7 +325,7 @@ void Video::sparseRetrace(int max) {
 	_lastSparse = timeKey;
 }
 
-void Video::drawPacked(byte *sprBuf, int16 width, int16 height,
+void Video::drawPacked(byte *sprBuf, int32 size, int16 width, int16 height,
 		int16 x, int16 y, byte transp, Surface &dest) {
 
 	int destRight = x + width;
@@ -371,13 +371,13 @@ void Video::drawPacked(byte *sprBuf, int16 width, int16 height,
 	}
 }
 
-void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height,
+void Video::drawPackedSprite(byte *sprBuf, int32 size, int16 width, int16 height,
 		int16 x, int16 y, int16 transp, Surface &dest) {
 
-	if (spriteUncompressor(sprBuf, width, height, x, y, transp, dest))
+	if (spriteUncompressor(sprBuf, size, width, height, x, y, transp, dest))
 		return;
 
-	drawPacked(sprBuf, width, height, x, y, transp, dest);
+	drawPacked(sprBuf,size,  width, height, x, y, transp, dest);
 }
 
 void Video::drawPackedSprite(const char *path, Surface &dest, int width) {
@@ -388,7 +388,7 @@ void Video::drawPackedSprite(const char *path, Surface &dest, int width) {
 		return;
 	}
 
-	drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest);
+	drawPackedSprite(data, size, width, dest.getHeight(), 0, 0, 0, dest);
 	delete[] data;
 }
 
diff --git a/engines/gob/video.h b/engines/gob/video.h
index 39f714a1d7e..de2987b648a 100644
--- a/engines/gob/video.h
+++ b/engines/gob/video.h
@@ -129,7 +129,7 @@ public:
 	void waitRetrace(bool mouse = true);
 	void sparseRetrace(int max);
 
-	void drawPackedSprite(byte *sprBuf, int16 width, int16 height,
+	void drawPackedSprite(byte *sprBuf, int32 size, int16 width, int16 height,
 			int16 x, int16 y, int16 transp, Surface &dest);
 	void drawPackedSprite(const char *path, Surface &dest, int width = 320);
 
@@ -152,7 +152,7 @@ public:
 	void dirtyRectsAdd(int16 left, int16 top, int16 right, int16 bottom);
 	void dirtyRectsApply(int left, int top, int width, int height, int x, int y);
 
-	virtual char spriteUncompressor(byte *sprBuf, int16 srcWidth,
+	virtual char spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth,
 			int16 srcHeight, int16 x, int16 y, int16 transp,
 			Surface &destDesc) = 0;
 
@@ -168,12 +168,12 @@ protected:
 
 	GobEngine *_vm;
 
-	void drawPacked(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Surface &dest);
+	void drawPacked(byte *sprBuf, int32 size, int16 width, int16 height, int16 x, int16 y, byte transp, Surface &dest);
 };
 
 class Video_v1 : public Video {
 public:
-	char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+	char spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 			int16 x, int16 y, int16 transp, Surface &destDesc) override;
 
 	Video_v1(GobEngine *vm);
@@ -182,7 +182,7 @@ public:
 
 class Video_v2 : public Video_v1 {
 public:
-	char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+	char spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 			int16 x, int16 y, int16 transp, Surface &destDesc) override;
 
 	Video_v2(GobEngine *vm);
@@ -191,14 +191,16 @@ public:
 
 class Video_v6 : public Video_v2 {
 public:
-	char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+	char spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 			int16 x, int16 y, int16 transp, Surface &destDesc) override;
 
 	Video_v6(GobEngine *vm);
 	~Video_v6() override {}
 
 private:
-	void drawPacked(const byte *sprBuf, int16 x, int16 y, Surface &surfDesc);
+	Graphics::PixelFormat _highColorPackedSpriteFormat;
+
+	void drawPacked(const byte *sprBuf, int32 size, int16 x, int16 y, Surface &surfDesc);
 	void drawYUVData(const byte *srcData, Surface &destDesc,
 			int16 width, int16 height, int16 x, int16 y);
 	void drawYUV(Surface &destDesc, int16 x, int16 y,
diff --git a/engines/gob/video_v1.cpp b/engines/gob/video_v1.cpp
index 8d40d4c04bd..cfef8a4d31c 100644
--- a/engines/gob/video_v1.cpp
+++ b/engines/gob/video_v1.cpp
@@ -35,7 +35,7 @@ namespace Gob {
 Video_v1::Video_v1(GobEngine *vm) : Video(vm) {
 }
 
-char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+char Video_v1::spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 	    int16 x, int16 y, int16 transp, Surface &destDesc) {
 	byte *memBuffer;
 	byte *srcPtr;
diff --git a/engines/gob/video_v2.cpp b/engines/gob/video_v2.cpp
index 783ac4d5c8e..05cdf0a3e9c 100644
--- a/engines/gob/video_v2.cpp
+++ b/engines/gob/video_v2.cpp
@@ -37,7 +37,7 @@ namespace Gob {
 Video_v2::Video_v2(GobEngine *vm) : Video_v1(vm) {
 }
 
-char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+char Video_v2::spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 	    int16 x, int16 y, int16 transp, Surface &destDesc) {
 	byte *memBuffer;
 	byte *srcPtr;
diff --git a/engines/gob/video_v6.cpp b/engines/gob/video_v6.cpp
index d56b8183e5a..6c476e3a1ab 100644
--- a/engines/gob/video_v6.cpp
+++ b/engines/gob/video_v6.cpp
@@ -28,24 +28,24 @@
 #include "common/endian.h"
 #include "common/savefile.h"
 
+#include "graphics/blit.h"
 #include "graphics/conversion.h"
 
+#include "gob/dataio.h"
 #include "gob/gob.h"
 #include "gob/video.h"
-#include "gob/util.h"
-#include "gob/draw.h"
-#include "gob/global.h"
 
 namespace Gob {
 
-Video_v6::Video_v6(GobEngine *vm) : Video_v2(vm) {
+Video_v6::Video_v6(GobEngine *vm) : Video_v2(vm), _highColorPackedSpriteFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)
+{
 }
 
-char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+char Video_v6::spriteUncompressor(byte *sprBuf, int32 size, int16 srcWidth, int16 srcHeight,
 	    int16 x, int16 y, int16 transp, Surface &destDesc) {
 
 	if ((sprBuf[0] == 1) && (sprBuf[1] == 3)) {
-		drawPacked(sprBuf, x, y, destDesc);
+		drawPacked(sprBuf, size, x, y, destDesc);
 		return 1;
 	}
 
@@ -55,10 +55,10 @@ char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
 	}
 
 	if ((sprBuf[0] == 1) && (sprBuf[1] == 2)) {
-		if (Video_v2::spriteUncompressor(sprBuf, srcWidth, srcHeight, x, y, transp, destDesc))
+		if (Video_v2::spriteUncompressor(sprBuf, size, srcWidth, srcHeight, x, y, transp, destDesc))
 			return 1;
 
-		Video::drawPacked(sprBuf, srcWidth, srcHeight, x, y, transp, destDesc);
+		Video::drawPacked(sprBuf, size, srcWidth, srcHeight, x, y, transp, destDesc);
 		return 1;
 	}
 
@@ -67,7 +67,7 @@ char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
 	return 1;
 }
 
-void Video_v6::drawPacked(const byte *sprBuf, int16 x, int16 y, Surface &surfDesc) {
+void Video_v6::drawPacked(const byte *sprBuf, int32 size, int16 x, int16 y, Surface &surfDesc) {
 	const byte *data = sprBuf + 2;
 
 	int16 width = READ_LE_UINT16(data);
@@ -75,22 +75,50 @@ void Video_v6::drawPacked(const byte *sprBuf, int16 x, int16 y, Surface &surfDes
 	data += 4;
 
 	const byte *srcData = data;
-	byte *uncBuf = nullptr;
-
-	if (*srcData++ != 0) {
-		uint32 size = READ_LE_UINT32(data);
-
-		uncBuf = new byte[size];
-
-		//spriteUncompressor(data, buf);
-		warning("Urban Stub: drawPacked: spriteUncompressor(data, uncBuf)");
+	byte dataType = *srcData++;
+
+	if (dataType == 0) {
+		// Uncompressed YUV data
+		drawYUVData(srcData, surfDesc, width, height, x, y);
+	} else if (dataType == 1) {
+		// Compressed YUV data
+		warning("drawPacked: untested case, compressed YUV data");
+		int32 uncompresedSize = 0;
+		byte *uncompressedData = DataIO::unpack(srcData, size, uncompresedSize, 1);
+		drawYUVData(uncompressedData, surfDesc, width, height, x, y);
+		delete uncompressedData;
+	} else if (dataType == 3) {
+		// Compressed high-color RGB data
+		int32 uncompresesSize = 0;
+		byte *uncompressedData = DataIO::unpack(srcData, size, uncompresesSize, 1);
+		Graphics::PixelFormat &format = _highColorPackedSpriteFormat;
+
+		if (_vm->getPixelFormat().aBits() > 0) {
+			// We need to force the transparent color 0 to be mapped to 0 in the target format
+			// First we fill the destination area with 0, then we will cross-blit skipping pixels with value 0 in the source
+			surfDesc.fillRectRaw(x, y, x + width - 1, y + height - 1, 0);
+		}
 
-		srcData = uncBuf;
+		bool conversionOk = false;
+		if (_vm->getPixelFormat().aBits() > 0) {
+			conversionOk = Graphics::crossKeyBlit(surfDesc.getData(x, y), uncompressedData,
+												  surfDesc.getWidth() * surfDesc.getBPP(), width * format.bytesPerPixel,
+												  width, height,
+												  _vm->getPixelFormat(), format, 0);
+		}
+		else
+			conversionOk = Graphics::crossBlit(surfDesc.getData(x, y), uncompressedData,
+											   surfDesc.getWidth() * surfDesc.getBPP(), width * format.bytesPerPixel,
+											   width, height,
+											   _vm->getPixelFormat(), format);
+
+		if (!conversionOk)
+			warning("drawPacked: error when cross-blitting from compressed RGB high-color data");
+
+		delete uncompressedData;
+	} else {
+		warning("drawPacked: unknown compression type %d", dataType);
 	}
-
-	drawYUVData(srcData, surfDesc, width, height, x, y);
-
-	delete[] uncBuf;
 }
 
 void Video_v6::drawYUVData(const byte *srcData, Surface &destDesc,


Commit: d77c08b9367ed3404cd935f83345306c3b5602d1
    https://github.com/scummvm/scummvm/commit/d77c08b9367ed3404cd935f83345306c3b5602d1
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Add some Adi4 save files

Changed paths:
    engines/gob/save/saveload.h
    engines/gob/save/saveload_v7.cpp


diff --git a/engines/gob/save/saveload.h b/engines/gob/save/saveload.h
index 477978cd646..d1c6b836f54 100644
--- a/engines/gob/save/saveload.h
+++ b/engines/gob/save/saveload.h
@@ -981,6 +981,10 @@ public:
 	static const uint32 kAdibou2NbrOfApplications = 7;
 	static const uint32 kAdibou2NbrOfSavedDrawings = 12;
 	static const uint32 kAdibou2NbrOfConstructionGameFiles = 3;
+
+	static const uint32 kAdi4NbrOfTempFiles = 9;
+	static const uint32 kAdi4NbrOfGameFiles = 36;
+
 	SaveLoad_v7(GobEngine *vm, const char *targetName);
 	~SaveLoad_v7() override;
 
@@ -1110,8 +1114,8 @@ protected:
 	GameFileHandler             *_adibou2AppProgressExtHandler[kAdibou2NbrOfApplications - 4][kChildrenCount];
 	GameFileHandler             *_adibou2AppliIniHandler[kAdibou2NbrOfApplications];
 
-	FakeFileHandler             *_addy4BaseHandler[2];
-	FakeFileHandler             *_addy4GrundschuleHandler[11];
+	FakeFileHandler             *_adi4TempFileHandler[kAdi4NbrOfTempFiles];
+	GameFileHandler             *_adi4GameFileHandler[kAdi4NbrOfGameFiles];
 
 	SaveHandler *getHandler(const char *fileName) const override;
 	const char *getDescription(const char *fileName) const override;
diff --git a/engines/gob/save/saveload_v7.cpp b/engines/gob/save/saveload_v7.cpp
index 18f21ec444f..fc1d69156a2 100644
--- a/engines/gob/save/saveload_v7.cpp
+++ b/engines/gob/save/saveload_v7.cpp
@@ -937,21 +937,58 @@ SaveLoad_v7::SaveFile SaveLoad_v7::_saveFiles[] = {
 	{"APPLIS/appli_06.ini", kSaveModeSave, nullptr, "app info" },
 	{"APPLIS/appli_07.ini", kSaveModeSave, nullptr, "app info" },
 
-	// Adi 4 / Addy 4 Base
-	{"DATA/config00.inf", kSaveModeSave, nullptr, nullptr        },
-	{"DATA/statev00.inf", kSaveModeSave, nullptr, nullptr        },
-	// Adi 4 / Addy 4 Grundschule
-	{ "premier.dep", kSaveModeSave, nullptr, nullptr        },
-	{ "quitter.dep", kSaveModeSave, nullptr, nullptr        },
-	{   "appel.dep", kSaveModeSave, nullptr, nullptr        },
-	{  "parole.dep", kSaveModeSave, nullptr, nullptr        },
-	{    "ado4.inf", kSaveModeSave, nullptr, nullptr        },
-	{"mcurrent.inf", kSaveModeSave, nullptr, nullptr        },
-	{   "perso.dep", kSaveModeSave, nullptr, nullptr        },
-	{ "nouveau.dep", kSaveModeSave, nullptr, nullptr        },
-	{     "adi.tmp", kSaveModeSave, nullptr, nullptr        },
-	{     "adi.inf", kSaveModeSave, nullptr, nullptr        },
-	{    "adi4.tmp", kSaveModeSave, nullptr, nullptr        },
+	// Adi 4
+	// Temporary ancillary files
+	{"DATA/premier.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/quitter.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/appel.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/parole.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/perso.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/nouveau.dep", kSaveModeSave, nullptr, nullptr},
+	{"DATA/iduser.tmp", kSaveModeSave, nullptr, nullptr},
+	{"adi.tmp", kSaveModeSave, nullptr, nullptr},
+	{"adi4.tmp", kSaveModeSave, nullptr, nullptr},
+
+	// Persitent files
+	{"DATA/iduser.inf", kSaveModeSave, nullptr, nullptr},
+	{"adi.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/ado4.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/mcurrent.inf", kSaveModeSave, nullptr, nullptr},
+
+	{"DATA/config00.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config01.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config02.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config03.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config04.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config05.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config06.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config07.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config08.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config09.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config10.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config10.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config12.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config13.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config14.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/config15.inf", kSaveModeSave, nullptr, nullptr},
+
+	{"DATA/statev00.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev01.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev02.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev03.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev04.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev05.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev06.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev07.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev08.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev09.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev10.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev10.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev12.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev13.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev14.inf", kSaveModeSave, nullptr, nullptr},
+	{"DATA/statev15.inf", kSaveModeSave, nullptr, nullptr},
+
 };
 
 SaveLoad_v7::SpriteHandler::File::File(GobEngine *vm, const Common::String &base, const Common::String &ext) :
@@ -1395,7 +1432,6 @@ SaveLoad_v7::SaveLoad_v7(GobEngine *vm, const char *targetName) :
 		_saveFiles[index++].handler = _adibou2AppIcoHandler[i] = new SpriteHandler(_vm, targetName, Common::String::format("app_ico%02d", i + 1));
 	}
 
-	const Common::Array<int> applisOffsets = {0, 4, 8, 12, 16, 20};
 	_saveFiles[index++].handler = _adibou2ApplicationsInfoHandler = new GameFileHandler(_vm, targetName, "applis");
 	_saveFiles[index++].handler = _adibou2LanceHandler = new FakeFileHandler(_vm);
 	_saveFiles[index++].handler = _adibou2RetourHandler = new FakeFileHandler(_vm);
@@ -1478,19 +1514,48 @@ SaveLoad_v7::SaveLoad_v7(GobEngine *vm, const char *targetName) :
 																					   Common::String::format("appli_%02d_ini", i + 1));
 	}
 
-	for (int i = 0; i < 2; i++)
-		_saveFiles[index++].handler = _addy4BaseHandler[i] = new FakeFileHandler(_vm);
+	for (uint32 i = 0; i < kAdi4NbrOfTempFiles; i++) {
+		_saveFiles[index++].handler = _adi4TempFileHandler[i] = new FakeFileHandler(_vm);
+	}
+
+	int indexAdi4file = 0;
+	_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																							  targetName,
+																							  "id_user");
+
+	_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																							  targetName,
+																							  "adi");
 
-	for (int i = 0; i < 11; i++)
-		_saveFiles[index++].handler = _addy4GrundschuleHandler[i] = new FakeFileHandler(_vm);
+	_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																							  targetName,
+																							  "ado4");
+
+	_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																							  targetName,
+																							  "mcurrent");
+
+	for (uint32 i = 0; i < kChildrenCount; i++) {
+		_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																								  targetName,
+																								  Common::String::format("config%02d", i));
+	}
+
+	for (uint32 i = 0; i < kChildrenCount; i++) {
+		_saveFiles[index++].handler = _adi4GameFileHandler[indexAdi4file++] = new GameFileHandler(_vm,
+																								  targetName,
+																								  Common::String::format("statev%02d", i));
+	}
 }
 
 SaveLoad_v7::~SaveLoad_v7() {
-	for (int i = 0; i < 11; i++)
-		delete _addy4GrundschuleHandler[i];
+	for (uint32 i = 0; i < kAdi4NbrOfTempFiles; i++) {
+		delete _adi4TempFileHandler[i];
+	}
 
-	for (int i = 0; i < 2; i++)
-		delete _addy4BaseHandler[i];
+	for (uint32 i = 0; i < kAdi4NbrOfGameFiles; i++) {
+		delete _adi4GameFileHandler[i];
+	}
 
 	delete _configHandler;
 	for (int i = 0; i < 4; i++)


Commit: 0f12e0c23301e0641f3bd38dc7061ebb93d06235
    https://github.com/scummvm/scummvm/commit/0f12e0c23301e0641f3bd38dc7061ebb93d06235
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Add Adi4 application directories to the search path

Changed paths:
    engines/gob/init_v7.cpp


diff --git a/engines/gob/init_v7.cpp b/engines/gob/init_v7.cpp
index f7416cb0736..c0a1a8510da 100644
--- a/engines/gob/init_v7.cpp
+++ b/engines/gob/init_v7.cpp
@@ -56,9 +56,25 @@ void Init_v7::initGame() {
 	gameDataDir.getChildren(subdirs, Common::FSNode::kListDirectoriesOnly);
 	for (const Common::FSNode &subdirNode : subdirs) {
 		Common::FSDirectory subdir(subdirNode);
-		if (subdir.hasFile("intro_ap.stk")) {
-			debugC(1, kDebugFileIO, "Found Adibou/Adi application subdirectory \"%s\", adding it to the search path", subdir.getFSNode().getName().c_str());
+		if (_vm->getGameType() == kGameTypeAdibou2 && subdir.hasFile("intro_ap.stk")) {
+			debugC(1, kDebugFileIO, "Found Adibou application subdirectory \"%s\", adding it to the search path", subdir.getFSNode().getName().c_str());
 			SearchMan.addSubDirectoryMatching(gameDataDir, subdir.getFSNode().getName(), 0, 4, true);
+		} else if (_vm->getGameType() == kGameTypeAdi4) {
+			// Look for any "DESC*.ADI" file
+			Common::FSList fileNodes;
+			subdirNode.getChildren(fileNodes, Common::FSNode::kListFilesOnly);
+			bool foundDescFile = false;
+			for (const Common::FSNode &fileNode : fileNodes) {
+				if (fileNode.getName().hasPrefixIgnoreCase("DESC") && fileNode.getName().hasSuffixIgnoreCase(".ADI")) {
+					debugC(1, kDebugFileIO, "Found Adi 4 application subdirectory \"%s\", adding it to the search path", subdir.getFSNode().getName().c_str());
+					SearchMan.addSubDirectoryMatching(gameDataDir, subdir.getFSNode().getName(), 0, 4, true);
+					foundDescFile = true;
+					break;
+				}
+			}
+
+			if (foundDescFile)
+				break;
 		}
 	}
 


Commit: 18de0ea90cadbb6fd66aabde5caad8691f8975c0
    https://github.com/scummvm/scummvm/commit/18de0ea90cadbb6fd66aabde5caad8691f8975c0
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:58+02:00

Commit Message:
GOB: Add o7_xorObfuscate and o7_xorDeobfuscate opcodes (Adi4)

Changed paths:
    engines/gob/inter.h
    engines/gob/inter_v7.cpp


diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index f640439e6fd..6a0f2d8f33c 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -704,6 +704,8 @@ protected:
 
 	Common::String ansiToOEM(Common::String string);
 	Common::String oemToANSI(Common::String string);
+	void xorObfuscate(byte *str, int len);
+	void xorDeobfuscate(byte *str, int len);
 
 	void o7_draw0x0C();
 	void o7_setCursorToLoadFromExec();
@@ -785,6 +787,8 @@ protected:
 	void o7_readData(OpFuncParams &params);
 	void o7_writeData(OpFuncParams &params);
 
+	void o7_xorDeobfuscate(OpGobParams &params);
+	void o7_xorObfuscate(OpGobParams &params);
 	void o7_ansiToOEM(OpGobParams &params);
 	void o7_oemToANSI(OpGobParams &params);
 	void o7_setDBStringEncoding(OpGobParams &params);
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index ea1b67de3a9..d58fcfcbdf0 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -152,6 +152,8 @@ void Inter_v7::setupOpcodesFunc() {
 void Inter_v7::setupOpcodesGob() {
 	Inter_Playtoons::setupOpcodesGob();
 
+	OPCODEGOB(407, o7_xorDeobfuscate);
+	OPCODEGOB(408, o7_xorObfuscate);
 	OPCODEGOB(420, o7_ansiToOEM);
 	OPCODEGOB(421, o7_oemToANSI);
 	OPCODEGOB(512, o7_setDBStringEncoding);
@@ -1962,6 +1964,37 @@ Common::String Inter_v7::oemToANSI(Common::String string) {
 	return string.decode(Common::kDos850).encode(Common::kWindows1252);
 }
 
+void Inter_v7::xorObfuscate(byte *str, int len) {
+	if (len <= 1)
+		return;
+
+	for (int i = len - 1; i >= 1; --i)
+		str[i] = str[i] ^ str[i - 1];
+}
+
+void Inter_v7::xorDeobfuscate(byte *str, int len) {
+	if (len <= 1)
+		return;
+
+	for (int i = 1; i < len; ++i)
+		str[i] = str[i] ^ str[i - 1];
+}
+
+
+void Inter_v7::o7_xorDeobfuscate(OpGobParams &params) {
+	uint16 varIndex = _vm->_game->_script->readUint16();
+	uint16 size = _vm->_game->_script->readUint16();
+	byte *data = _vm->_inter->_variables->getAddressVar8(varIndex);
+	xorDeobfuscate(data, size);
+}
+
+void Inter_v7::o7_xorObfuscate(OpGobParams &params) {
+	uint16 varIndex = _vm->_game->_script->readUint16();
+	byte *data = _vm->_inter->_variables->getAddressVar8(varIndex);
+	uint16 size = _vm->_game->_script->readUint16();
+	xorObfuscate(data, size);
+}
+
 void Inter_v7::o7_ansiToOEM(OpGobParams &params) {
 	uint16 varIndex = _vm->_game->_script->readUint16();
 	char *str = GET_VAR_STR(varIndex);


Commit: 82d3bb2d49562aeb434afc949343d80b77b7c98d
    https://github.com/scummvm/scummvm/commit/82d3bb2d49562aeb434afc949343d80b77b7c98d
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:59+02:00

Commit Message:
GOB: Add a missing dummy opcode used in Adi4

Changed paths:
    engines/gob/inter.h
    engines/gob/inter_v7.cpp


diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index 6a0f2d8f33c..be1b82bb3fd 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -794,6 +794,7 @@ protected:
 	void o7_setDBStringEncoding(OpGobParams &params);
 	void o7_gob0x201(OpGobParams &params);
 	void o7_getFreeDiskSpace(OpGobParams &params);
+	void o7_dummy(OpGobParams &params);
 
 private:
 	INIConfig _inis;
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index d58fcfcbdf0..95f7631f4ba 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -159,6 +159,8 @@ void Inter_v7::setupOpcodesGob() {
 	OPCODEGOB(512, o7_setDBStringEncoding);
 	OPCODEGOB(513, o7_gob0x201);
 	OPCODEGOB(600, o7_getFreeDiskSpace);
+
+	OPCODEGOB(782, o7_dummy);
 }
 
 void Inter_v7::o7_draw0x0C() {
@@ -2038,5 +2040,9 @@ void Inter_v7::o7_getFreeDiskSpace(OpGobParams &params) {
 	WRITE_VAR(varIndex, 1000000000); // HACK
 }
 
+void Inter_v7::o7_dummy(OpGobParams &params) {
+	_vm->_game->_script->skip(4);
+}
+
 
 } // End of namespace Gob


Commit: cd52af636d922f7aa5e7b15a0a13cfa54e01e34f
    https://github.com/scummvm/scummvm/commit/cd52af636d922f7aa5e7b15a0a13cfa54e01e34f
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2025-08-12T17:30:59+02:00

Commit Message:
GOB: Reorganize Adi4 add-ons by age ranges

Changed paths:
    engines/gob/detection/tables.h
    engines/gob/detection/tables_adi4.h


diff --git a/engines/gob/detection/tables.h b/engines/gob/detection/tables.h
index 00df942f099..380eb155283 100644
--- a/engines/gob/detection/tables.h
+++ b/engines/gob/detection/tables.h
@@ -71,9 +71,22 @@ static const PlainGameDescriptor gobGames[] = {
 	{"adi1", "ADI 1"},
 	{"adi2", "ADI 2"},
 	{"adi4", "ADI 4"},
-	{"adi4geo", "ADI 4 Geographie"},
-	{"adi4anglais", "ADI 4 Anglais"},
-	{"adi4mathlanguage", "ADI 4 Math & Language"},
+	{"adi4mathlanguage78", "ADI 4 Math & Language 7-8 years"},
+	{"adi4mathlanguage89", "ADI 4 Math & Language 8-9 years"},
+	{"adi4mathlanguage910", "ADI 4 Math & Language 9-10 years"},
+	{"adi4mathlanguage1011", "ADI 4 Math & Language 10-11 years"},
+	{"adi4mathlanguage1112", "ADI 4 Math & Language 11-12 years"},
+	{"adi4mathlanguage1213", "ADI 4 Math & Language 12-13 years"},
+	{"adi4mathlanguage1314", "ADI 4 Math & Language 13-14 years"},
+	{"adi4mathlanguage1415", "ADI 4 Math & Language 14-15 years"},
+	{"adi4anglais79", "ADI 4 Anglais 7-9 years"},
+	{"adi4anglais911", "ADI 4 Anglais 9-11 years"},
+	{"adi4anglais1112", "ADI 4 Anglais 11-12 years"},
+	{"adi4anglais1213", "ADI 4 Anglais 12-13 years"},
+	{"adi4anglais1314", "ADI 4 Anglais 13-14 years"},
+	{"adi4anglais1415", "ADI 4 Anglais 14-15 years"},
+	{"adi4geo", "ADI 4 Geography"},
+	{"adi4sciences", "ADI 4 Sciences"},
 	{"adi4euro", "ADI 4 Euro"},
 	{"adi5", "ADI 5"},
 	{"adi5language", "ADI 5 Language"},
diff --git a/engines/gob/detection/tables_adi4.h b/engines/gob/detection/tables_adi4.h
index 84256d462df..54ec8d2ed41 100644
--- a/engines/gob/detection/tables_adi4.h
+++ b/engines/gob/detection/tables_adi4.h
@@ -239,13 +239,15 @@
 	0, 0, 0
 },
 
-// -- Add-ons : Geographie --
+
+// -- Add-ons : "Math & Language" --
+// 8-9 years
 {
 	{
-		"adi4geo",
-		"", // Geographie
-		AD_ENTRY2s("INTROGEO.STK", "d86d0f53818dd285bebff25925627b8c", 3170680,
-				   "INTROGEO.ITK", "5daacbf8840f811e48b99e1d92933873", 20084736),
+		"adi4mathlanguage89",
+		"", // Français Maths CE2
+		AD_ENTRY2s("ADIF91.STK", "f5e4d0e38e96cb9ea3fdb122548a7775", 19707056,
+				   "ADIM91.STK", "2f839dffcded30680456d0e881f16f31", 27322360),
 		FR_FRA,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
@@ -254,13 +256,17 @@
 	kFeatures640x480,
 	0, 0, 0
 },
+
+// 9-10 years
+
+// 10-11 years
 {
 	{
-		"adi4geo",
-		"", // Erdkunde
-		AD_ENTRY2s("INTROGEO.STK", "f01ffe9366df86a7ea5ed425b41081ba", 3284478,
-				   "INTROGEO.ITK", "998bb8e759d5b8b4e7aa22d6030f2dad", 22046720),
-		DE_DEU,
+		"adi4mathlanguage1011",
+		"", // Français Maths CM2
+		AD_ENTRY2s("ADIF71.STK", "a0dc766e42025271df54f4e705e530e5", 13668920,
+				   "ADIM71.STK", "d093bf3b38c668d9f89ae1118b2dfc95", 21544420),
+		FR_FRA,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
 		GUIO0()
@@ -269,15 +275,25 @@
 	0, 0, 0
 },
 
+// 11-12 years
 
-// -- Add-ons : Euro --
+// 12-13 years
+
+// 13-14 years
+
+// 14-15 years
+
+// -- Add-ons : "Anglais" (English for non-native speakers) --
+// 7-9 years
+
+// 9-11 years
 {
 	{
-		"adi4euro",
-		"", // Der Euro
-		AD_ENTRY2s("EURO.STK", "7dac3823570036c6eda57cc2c872aa59", 681944,
-				   "EURO.ITK", "09629a0aa35a00f68211f6429bd43e9f", 25409536),
-		DE_DEU,
+		"adi4anglais911",
+		"",
+		AD_ENTRY2s("A71RAN.STK", "1c16f54d71ed3d2fa49fe4d8ff4884ae", 100144,
+				   "ADIA71.STK", "9cc17a7ccbf157c1742387ce133205fd", 21661580),
+		FR_FRA,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
 		GUIO0()
@@ -286,13 +302,21 @@
 	0, 0, 0
 },
 
-// -- Add-ons : "Anglais" (English for non-native speakers) --
+// 11-12 years
+
+// 12-13 years
+
+// 13-14 years
+
+// 14-15 years
+
+// -- Add-ons : Geography --
 {
 	{
-		"adi4anglais",
-		"",
-		AD_ENTRY2s("A71RAN.STK", "1c16f54d71ed3d2fa49fe4d8ff4884ae", 100144,
-				   "ADIA71.STK", "9cc17a7ccbf157c1742387ce133205fd", 21661580),
+		"adi4geo",
+		"", // Géographie
+		AD_ENTRY2s("INTROGEO.STK", "d86d0f53818dd285bebff25925627b8c", 3170680,
+				   "INTROGEO.ITK", "5daacbf8840f811e48b99e1d92933873", 20084736),
 		FR_FRA,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
@@ -302,14 +326,13 @@
 	0, 0, 0
 },
 
-// -- Add-ons : "Math & Language" --
 {
 	{
-		"adi4mathlanguage",
-		"", // Français Math CE2
-		AD_ENTRY2s("ADIF91.STK", "f5e4d0e38e96cb9ea3fdb122548a7775", 19707056,
-				   "ADIM91.STK", "2f839dffcded30680456d0e881f16f31", 27322360),
-		FR_FRA,
+		"adi4geo",
+		"", // Erdkunde
+		AD_ENTRY2s("INTROGEO.STK", "f01ffe9366df86a7ea5ed425b41081ba", 3284478,
+				   "INTROGEO.ITK", "998bb8e759d5b8b4e7aa22d6030f2dad", 22046720),
+		DE_DEU,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
 		GUIO0()
@@ -317,13 +340,17 @@
 	kFeatures640x480,
 	0, 0, 0
 },
+
+// -- Add-ons : Sciences --
+
+// -- Add-ons : Euro --
 {
 	{
-		"adi4mathlanguage",
-		"", // Français Math CM2
-		AD_ENTRY2s("ADIF71.STK", "a0dc766e42025271df54f4e705e530e5", 13668920,
-				   "ADIM71.STK", "d093bf3b38c668d9f89ae1118b2dfc95", 21544420),
-		FR_FRA,
+		"adi4euro",
+		"", // Der Euro
+		AD_ENTRY2s("EURO.STK", "7dac3823570036c6eda57cc2c872aa59", 681944,
+				   "EURO.ITK", "09629a0aa35a00f68211f6429bd43e9f", 25409536),
+		DE_DEU,
 		kPlatformWindows,
 		ADGF_ADDON | ADGF_UNSTABLE,
 		GUIO0()




More information about the Scummvm-git-logs mailing list