[Scummvm-git-logs] scummvm master -> 4e9b273bd646ec4a209668e87e8fabd32069ef9c

sdelamarre noreply at scummvm.org
Fri Apr 17 20:58:03 UTC 2026


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

Summary:
4354229d95 GOB: Implement o7_getVmdCurrentFrameRect (Adi4)
f8024e1096 GOB: Implement o7_calculator (Adi4)
2043a7edad GOB: Fix a transparency bug with 32-bits pixel formats
9b02e43a11 GOB: Fix underline width in printTotText
584cb3d7d0 GOB: Fix o2_addHotspot behavior for Adibou2/Adi4
1119022d3b GOB: Fix swapped width/height issue in an error message
4e9b273bd6 GOB: Fix a video glitch in Adi4 applications


Commit: 4354229d95b6161d5324a8bd22404a550f5554d5
    https://github.com/scummvm/scummvm/commit/4354229d95b6161d5324a8bd22404a550f5554d5
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:26+02:00

Commit Message:
GOB: Implement o7_getVmdCurrentFrameRect (Adi4)

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


diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index fcdf026d009..605594c1933 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -731,6 +731,7 @@ protected:
 	void o7_findNextFile();
 	void o7_getFileInfo();
 	void o7_getSystemProperty();
+	void o7_getVmdCurrentFrameRect();
 	void o7_loadImage();
 	void o7_copyDataToClipboard();
 	void o7_setVolume();
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index 938c925030a..024ed9c8415 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -87,6 +87,7 @@ void Inter_v7::setupOpcodesDraw() {
 	OPCODEDRAW(0x8A, o7_findFile);
 	OPCODEDRAW(0x8B, o7_findNextFile);
 	OPCODEDRAW(0x8C, o7_getSystemProperty);
+	OPCODEDRAW(0x8D, o7_getVmdCurrentFrameRect);
 	OPCODEDRAW(0x8E, o7_getFileInfo);
 	OPCODEDRAW(0x90, o7_loadImage);
 	OPCODEDRAW(0x91, o7_copyDataToClipboard);
@@ -993,6 +994,24 @@ void Inter_v7::o7_findNextFile() {
 	storeValue(file.empty() ? 0 : 1);
 }
 
+void Inter_v7::o7_getVmdCurrentFrameRect() {
+	int16 slot = _vm->_game->_script->readValExpr();
+
+	int16 x, y, width, height;
+	if (slot >= 0 && slot < 7 && _vm->_vidPlayer->slotIsOpen(slot) &&
+			_vm->_vidPlayer->getFrameCoords(slot, _vm->_vidPlayer->getCurrentFrame(slot), x, y, width, height)) {
+		storeValue((uint32)x);                    // left
+		storeValue((uint32)(x + width - 1));      // right
+		storeValue((uint32)y);                    // top
+		storeValue((uint32)(y + height - 1));     // bottom
+	} else {
+		storeValue((uint32)-1);
+		storeValue((uint32)-1);
+		storeValue((uint32)-1);
+		storeValue((uint32)-1);
+	}
+}
+
 void Inter_v7::o7_getSystemProperty() {
 	const char *property = _vm->_game->_script->evalString();
 	if (!scumm_stricmp(property, "TotalPhys")) {
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 0d91e671318..a493dcde250 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -1066,6 +1066,14 @@ Common::SeekableReadStream *VideoPlayer::getEmbeddedFile(const Common::String &f
 	return video->decoder->getEmbeddedFile(fileName);
 }
 
+bool VideoPlayer::getFrameCoords(int slot, int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) const {
+	const Video *video = getVideoBySlot(slot);
+	if (!video)
+		return false;
+
+	return video->decoder->getFrameCoords(frame, x, y, width, height);
+}
+
 int32 VideoPlayer::getSubtitleIndex(int slot) const {
 	const Video *video = getVideoBySlot(slot);
 	if (!video)
diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h
index 8048b85e374..b038882d291 100644
--- a/engines/gob/videoplayer.h
+++ b/engines/gob/videoplayer.h
@@ -159,6 +159,8 @@ public:
 	bool   hasVideo          (int slot = 0) const;
 
 
+	bool getFrameCoords(int slot, int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) const;
+
 	const Common::List<Common::Rect> *getDirtyRects(int slot = 0) const;
 
 	bool                      hasEmbeddedFile(const Common::String &fileName, int slot = 0) const;


Commit: f8024e1096a67a583dc4f5bd21769304bc552e71
    https://github.com/scummvm/scummvm/commit/f8024e1096a67a583dc4f5bd21769304bc552e71
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:26+02:00

Commit Message:
GOB: Implement o7_calculator (Adi4)

Used in the "calculator" tool on Adi's desk.

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


diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index 605594c1933..91a5ae58e27 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -816,6 +816,7 @@ protected:
 	void o7_setDBStringEncoding(OpGobParams &params);
 	void o7_gob0x201(OpGobParams &params);
 	void o7_getFreeDiskSpace(OpGobParams &params);
+	void o7_calculator(OpGobParams &params);
 	void o7_dummy(OpGobParams &params);
 
 private:
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index 024ed9c8415..4ad97f52e2b 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -167,6 +167,7 @@ void Inter_v7::setupOpcodesGob() {
 	OPCODEGOB(410, o7_resolvePath);
 	OPCODEGOB(420, o7_ansiToOEM);
 	OPCODEGOB(421, o7_oemToANSI);
+	OPCODEGOB(457, o7_calculator);
 	OPCODEGOB(512, o7_setDBStringEncoding);
 	OPCODEGOB(513, o7_gob0x201);
 	OPCODEGOB(600, o7_getFreeDiskSpace);
@@ -2417,6 +2418,153 @@ void Inter_v7::o7_gob0x201(OpGobParams &params) {
 	WRITE_VAR(varIndex, 1);
 }
 
+enum CalcOp {
+	kCalcAdd       = 1,
+	kCalcSubtract  = 2,
+	kCalcDivide    = 3,
+	kCalcMultiply  = 4,
+	kCalcInverse   = 5,
+	kCalcSquare    = 6,
+	kCalcSqrt      = 7,
+	kCalcPower     = 8,
+	kCalcExp       = 9,
+	kCalcSin       = 10,
+	kCalcTan       = 11,
+	kCalcAcos      = 12,
+	kCalcAsin      = 13,
+	kCalcLn        = 14
+};
+
+enum CalcError {
+	kCalcErrNone       = 0,
+	kCalcErrInf        = 2,
+	kCalcErrNaN        = 3,
+	kCalcErrDomain     = 4,
+	kCalcErrDivByZero  = 5,
+	kCalcErrFPU        = 6
+};
+
+void Inter_v7::o7_calculator(OpGobParams &params) {
+	// Calculator opcode, used by the Adi4 calculator tool.
+
+	uint16 operandAAndResultVarIndex = _vm->_game->_script->readUint16();
+	uint16 operandBVarIndex = _vm->_game->_script->readUint16();
+	uint16 operationVarIndex = _vm->_game->_script->readUint16();
+	int operation = VAR(operationVarIndex);
+
+	const char *strA = _vm->_inter->_variables->getAddressVarString(operandAAndResultVarIndex);
+	const char *strB = _vm->_inter->_variables->getAddressVarString(operandBVarIndex);
+	double a = atof(strA);
+	double b = atof(strB);
+	double result = 0.0;
+	int errorCode = kCalcErrNone;
+
+	switch (operation) {
+	case kCalcAdd:
+		result = a + b;
+		break;
+
+	case kCalcSubtract:
+		result = a - b;
+		break;
+
+	case kCalcDivide:
+		if (b == 0.0)
+			errorCode = kCalcErrDivByZero;
+		else
+			result = a / b;
+		break;
+
+	case kCalcMultiply:
+		result = a * b;
+		break;
+
+	case kCalcInverse:
+		if (b == 0.0)
+			errorCode = kCalcErrDivByZero;
+		else
+			result = 1.0 / b;
+		break;
+
+	case kCalcSquare:
+		result = pow(b, 2.0);
+		break;
+
+	case kCalcSqrt:
+		if (b < 0.0)
+			errorCode = kCalcErrDomain;
+		else
+			result = sqrt(b);
+		break;
+
+	case kCalcPower:
+		if (a == 0.0 && b == 0.0)
+			result = 1.0;
+		else
+			result = pow(a, b);
+		break;
+
+	case kCalcExp:
+		result = exp(b);
+		break;
+
+	case kCalcSin:
+		result = sin(b);
+		break;
+
+	case kCalcTan:
+		if (sin(b) == 0.0)
+			errorCode = kCalcErrDomain;
+		else
+			result = tan(b);
+		break;
+
+	case kCalcAcos:
+		if (b < 0.0 || b > 1.0)
+			errorCode = kCalcErrDomain;
+		else
+			result = acos(b);
+		break;
+
+	case kCalcAsin:
+		if (b < 0.0 || b > 1.0)
+			errorCode = kCalcErrDomain;
+		else
+			result = asin(b);
+		break;
+
+	case kCalcLn:
+		result = log(b);
+		break;
+
+	default:
+		warning("o7_calculator: unknown operation %d", operation);
+		break;
+	}
+
+	if (errorCode == kCalcErrNone) {
+		// Check for overflow/special values in the formatted result
+		Common::String resultFormatted = Common::String::format("%-12g", result);
+		if (resultFormatted.contains("nan") || resultFormatted.contains("NAN")) {
+			errorCode = kCalcErrNaN;
+			result = 0.0;
+		} else if (resultFormatted.contains("inf") || resultFormatted.contains("INF")) {
+			errorCode = kCalcErrInf;
+			result = 0.0;
+		}
+	}
+
+	uint8 *resultData = _vm->_inter->_variables->getAddressVar8(operandAAndResultVarIndex);
+	if (errorCode != kCalcErrNone) {
+		// Error: write empty string + error code in second byte
+		resultData[0] = '\0';
+		resultData[1] = (uint8)errorCode;
+	} else {
+		Common::String formatted = Common::String::format("%-12g", result);
+		WRITE_VAR_STR(operandAAndResultVarIndex, formatted.c_str());
+	}
+}
+
 void Inter_v7::o7_getFreeDiskSpace(OpGobParams &params) {
 	// This opcode is called by the game scripts to check if there is enough free space on the hard disk, before
 	// copying some data from the CD (e.g. when starting Adibou2/Sciences for the first time).


Commit: 2043a7edad6fc738e86c1e99c4fcd5f7375338cd
    https://github.com/scummvm/scummvm/commit/2043a7edad6fc738e86c1e99c4fcd5f7375338cd
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:27+02:00

Commit Message:
GOB: Fix a transparency bug with 32-bits pixel formats

The "-1" sentinel meaning "no transparency" overlapped with pure white
0xFFFFFF, resulting in transparency bugs.

Move the per-pixel Surface::blit into a templated helper, distinguishing
properly the "transparency disabled" case at compile time, instead of
comparing the pixels with the sentinel assuming they will never be -1.

This fixes a transparency bug in Adi4's calculator for backends with
a 32-bits Pixel format.

Changed paths:
    engines/gob/surface.cpp


diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp
index e433b7af63f..452492a62f1 100644
--- a/engines/gob/surface.cpp
+++ b/engines/gob/surface.cpp
@@ -364,6 +364,40 @@ uint32 Surface::getColorFromIndex(uint8 index) const {
 		return _highColorMap[index];
 }
 
+template<bool yAxisReflection, bool kTransp, bool kSameBpp>
+static void blitPixels(Pixel dst, ConstPixel src,
+					   uint16 width, uint16 height,
+					   int16 dstPitch, int16 srcPitch,
+					   int32 transp, const uint32 *highColorMap) {
+	while (height-- > 0) {
+		Pixel dstRow = dst;
+		ConstPixel srcRow = src;
+
+		if (yAxisReflection)
+			srcRow += width - 1;
+
+		for (uint16 i = 0; i < width; i++, ++dstRow) {
+			uint32 pixel = srcRow.get();
+
+			if (yAxisReflection)
+				--srcRow;
+			else
+				++srcRow;
+
+			if (kTransp && pixel == (uint32)transp)
+				continue;
+
+			if (kSameBpp)
+				dstRow.set(pixel);
+			else
+				dstRow.set(highColorMap[pixel]);
+		}
+
+		dst += dstPitch;
+		src += srcPitch;
+	}
+}
+
 void Surface::blit(const Surface &from, int16 left, int16 top, int16 right, int16 bottom,
 		int16 x, int16 y, int32 transp, bool yAxisReflection) {
 
@@ -399,7 +433,7 @@ void Surface::blit(const Surface &from, int16 left, int16 top, int16 right, int1
 		return;
 	}
 
-	if (transp == -1 && !yAxisReflection && from._bpp == _bpp && _bpp == 1) {
+	if (transp == -1 && !yAxisReflection && from._bpp == _bpp) {
 		// We don't have to look for transparency => we can use memmove line-wise
 
 		// Pointers to the blit destination and source start points
@@ -422,37 +456,30 @@ void Surface::blit(const Surface &from, int16 left, int16 top, int16 right, int1
 	     Pixel dst =      get(x   , y);
 	ConstPixel src = from.get(left, top);
 
-	while (height-- > 0) {
-		     Pixel dstRow = dst;
-		ConstPixel srcRow = src;
-
-		if (yAxisReflection) {
-			srcRow += width - 1;
-			for (uint16 i = 0; i < width; i++, ++dstRow, --srcRow) {
-				if (srcRow.get() != ((uint32) transp)) {
-					if (_bpp == from._bpp)
-						dstRow.set(srcRow.get());
-					else {
-						uint32 index = srcRow.get();
-						dstRow.set(from._highColorMap[index]);
-					}
-				}
-			}
+	if (yAxisReflection) {
+		if (transp == -1) {
+			if (_bpp == from._bpp)
+				blitPixels<true, false, true>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+			else
+				blitPixels<true, false, false>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
 		} else {
-			for (uint16 i = 0; i < width; i++, ++dstRow, ++srcRow) {
-				if (srcRow.get() != ((uint32) transp)) {
-					if (_bpp == from._bpp)
-						dstRow.set(srcRow.get());
-					else {
-						uint32 index = srcRow.get();
-						dstRow.set(from._highColorMap[index]);
-					}
-				}
-			}
+			if (_bpp == from._bpp)
+				blitPixels<true, true, true>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+			else
+				blitPixels<true, true, false>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+		}
+	} else {
+		if (transp == -1) {
+			if (_bpp == from._bpp)
+				blitPixels<false, false, true>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+			else
+				blitPixels<false, false, false>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+		} else {
+			if (_bpp == from._bpp)
+				blitPixels<false, true, true>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
+			else
+				blitPixels<false, true, false>(dst, src, width, height, _width, from._width, transp, from._highColorMap);
 		}
-
-		dst +=      _width;
-		src += from._width;
 	}
 }
 
@@ -571,7 +598,7 @@ void Surface::blitShaded(const Surface &from, int16 left, int16 top, int16 right
 		     Pixel dstRow = dst;
 		ConstPixel srcRow = src;
 		for (uint16 i = 0; i < width; i++, ++dstRow, ++srcRow) {
-			if (srcRow.get() == ((uint32) transp))
+			if (transp != -1 && srcRow.get() == ((uint32) transp))
 				continue;
 
 			uint8 srcR = 0;


Commit: 9b02e43a117fb85ca751cf2a38fc52dcd7fae8e2
    https://github.com/scummvm/scummvm/commit/9b02e43a117fb85ca751cf2a38fc52dcd7fae8e2
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:27+02:00

Commit Message:
GOB: Fix underline width in printTotText

This fixes incorrect underlined text in the "Calendar - Centuries"
activity of "Adi4/Math CE2" application.

Changed paths:
    engines/gob/draw_playtoons.cpp


diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 5f9a8faf4b8..95b0646543c 100644
--- a/engines/gob/draw_playtoons.cpp
+++ b/engines/gob/draw_playtoons.cpp
@@ -346,7 +346,7 @@ void Draw_Playtoons::printTotText(int16 id) {
 					rectTop = _fonts[fontIndex]->getCharHeight();
 					adjustCoords(1, nullptr, &rectTop);
 
-					_spriteRight = _destSpriteX + rectLeft - 1;
+					_spriteRight = rectLeft - 1;
 					_spriteBottom = offY + rectTop;
 					_destSpriteY = _spriteBottom;
 					spriteOperation(DRAW_DRAWLINE);


Commit: 584cb3d7d09fa5161beb87eee8dedfce9181654e
    https://github.com/scummvm/scummvm/commit/584cb3d7d09fa5161beb87eee8dedfce9181654e
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:27+02:00

Commit Message:
GOB: Fix o2_addHotspot behavior for Adibou2/Adi4

Fixes a bug in "Draw symmetric figure" exercise in the "Maths CE2" Adi4
application, where points and lines where not aligned with the
background grid.

Changed paths:
    engines/gob/inter_v2.cpp


diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp
index eaa61e211f5..43ea67d4b4b 100644
--- a/engines/gob/inter_v2.cpp
+++ b/engines/gob/inter_v2.cpp
@@ -1207,10 +1207,21 @@ void Inter_v2::o2_addHotspot(OpFuncParams &params) {
 		top     = 0;
 	}
 
-	if (id < 0)
-		_vm->_game->_hotspots->add(0xD000 - id, left & 0xFFFC, top & 0xFFFC,
+	if (id < 0) {
+		int16 hotspotLeft = 0;
+		int16 hotspotTop = 0;
+		if (_vm->getGameType() == kGameTypeAdibou2 || _vm->getGameType() == kGameTypeAdi4) {
+			// The operation is no longer a "floor to previous multiple of 4", but a "minus 4"
+			// NOTE: may be needed by other games as well
+			hotspotLeft = left - 4;
+			hotspotTop  = top  - 4;
+		} else {
+			hotspotLeft = left & 0xFFFC;
+			hotspotTop  = top  & 0xFFFC;
+		}
+		_vm->_game->_hotspots->add(0xD000 - id, hotspotLeft, hotspotTop,
 				left + width + 3, top + height + 3, flags, key, 0, 0, funcPos);
-	else
+	} else
 		_vm->_game->_hotspots->add(0xE000 + id, left, top,
 				left + width - 1, top + height - 1, flags, key, 0, 0, funcPos);
 }


Commit: 1119022d3b8117229efb8a29dd831c54069edcad
    https://github.com/scummvm/scummvm/commit/1119022d3b8117229efb8a29dd831c54069edcad
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:27+02:00

Commit Message:
GOB: Fix swapped width/height issue in an error message

Changed paths:
    engines/gob/videoplayer.cpp


diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index a493dcde250..fac7afb5c45 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -234,14 +234,12 @@ int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties
 			bool screenSize = properties.flags & kFlagScreenSurface;
 
 			if (ownSurf) {
-				uint16 height = screenSize ? _vm->_width  : video->decoder->getWidth();
-				uint16 width = screenSize ? _vm->_height : video->decoder->getHeight();
+				uint16 width = screenSize ? _vm->_width  : video->decoder->getWidth();
+				uint16 height = screenSize ? _vm->_height : video->decoder->getHeight();
 
 				if (height > 0 && width > 0) {
 					_vm->_draw->_spritesArray[properties.sprite] =
-						_vm->_video->initSurfDesc(screenSize ? _vm->_width  : video->decoder->getWidth(),
-												  screenSize ? _vm->_height : video->decoder->getHeight(), 0,
-												  0);
+						_vm->_video->initSurfDesc(width, height, 0, 0);
 				} else {
 					warning("VideoPlayer::openVideo() file=%s:"
 							"Invalid surface dimensions (%dx%d)", file.c_str(), width, height);


Commit: 4e9b273bd646ec4a209668e87e8fabd32069ef9c
    https://github.com/scummvm/scummvm/commit/4e9b273bd646ec4a209668e87e8fabd32069ef9c
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-04-17T22:57:27+02:00

Commit Message:
GOB: Fix a video glitch in Adi4 applications

Some Adi's animations, when Adi is waiting for a player action, were not
displayed at their correct location. This was due to a special case in
o2_getImdInfo that have been removed in later versions of the engine.

Changed paths:
    engines/gob/videoplayer.cpp


diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index fac7afb5c45..01b75bad8d0 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -1096,8 +1096,11 @@ void VideoPlayer::writeVideoInfo(const Common::String &file, uint16 varX, uint16
 		width  = video.decoder->getWidth();
 		height = video.decoder->getHeight();
 
-		if (VAR_OFFSET(varX) == 0xFFFFFFFF)
-			video.decoder->getFrameCoords(1, x, y, width, height);
+		if (_vm->getGameType() != kGameTypeAdibou2 && _vm->getGameType() != kGameTypeAdi4) {
+			// Note: not found in Adibou2/Adi4 disasm, and cause video gltiches
+			if (VAR_OFFSET(varX) == 0xFFFFFFFF)
+				video.decoder->getFrameCoords(1, x, y, width, height);
+		}
 
 		WRITE_VAR_OFFSET(varX     , x);
 		WRITE_VAR_OFFSET(varY     , y);




More information about the Scummvm-git-logs mailing list