[Scummvm-git-logs] scummvm master -> 5e7d56f783317f8f1ba2737c9edcdbbadf370adb

sdelamarre noreply at scummvm.org
Sat Feb 28 11:39:18 UTC 2026


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

Summary:
9336278b45 IMAGE: Handle ANI files wrongly including header size in the chunk size field
5e7d56f783 GOB: Handle animated cursors from .ANI files in Adibou2/Adi4


Commit: 9336278b454720da4f6288ed475fd56d09b44a2b
    https://github.com/scummvm/scummvm/commit/9336278b454720da4f6288ed475fd56d09b44a2b
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-02-28T12:39:10+01:00

Commit Message:
IMAGE: Handle ANI files wrongly including header size in the chunk size field

Found for example in Adi4's waiting cursor "WAIT.ANI"

Changed paths:
    image/ani.cpp


diff --git a/image/ani.cpp b/image/ani.cpp
index f662292205b..218a30b55e6 100644
--- a/image/ani.cpp
+++ b/image/ani.cpp
@@ -125,8 +125,15 @@ bool AniDecoder::parseRIFFChunks(Common::SeekableReadStream &stream, const RIFFC
 
 		int64 chunkAvailable = stream.size() - stream.pos();
 		if (chunkAvailable < actualChunkSize) {
-			warning("AniDecoder::parseRIFFChunk: RIFF chunk is too large");
-			return false;
+			if (chunkAvailable == chunkSize - 8) {
+				// Some ANI files wrongly include the 8-byte chunk header size in the chunk size field, tolerate that
+				warning("AniDecoder::parseRIFFChunks: Chunk size seems to include header size, clamping");
+				chunkSize = static_cast<uint32>(chunkAvailable);
+				actualChunkSize = chunkSize;
+			} else {
+				warning("AniDecoder::parseRIFFChunk: RIFF chunk is too large");
+				return false;
+			}
 		}
 
 		RIFFChunkDef chunkDef;


Commit: 5e7d56f783317f8f1ba2737c9edcdbbadf370adb
    https://github.com/scummvm/scummvm/commit/5e7d56f783317f8f1ba2737c9edcdbbadf370adb
Author: Simon Delamarre (simon.delamarre14 at gmail.com)
Date: 2026-02-28T12:39:10+01:00

Commit Message:
GOB: Handle animated cursors from .ANI files in Adibou2/Adi4

Changed paths:
    engines/gob/draw.h
    engines/gob/draw_v7.cpp
    engines/gob/videoplayer.cpp


diff --git a/engines/gob/draw.h b/engines/gob/draw.h
index 5857fe50125..186147b5d9d 100644
--- a/engines/gob/draw.h
+++ b/engines/gob/draw.h
@@ -35,6 +35,10 @@ namespace Common {
 class WinResources;
 }
 
+namespace Image {
+class AniDecoder;
+}
+
 namespace Gob {
 
 #define RENDERFLAG_NOINVALIDATE      0x0001
@@ -225,6 +229,7 @@ public:
 	virtual void blitCursor() = 0;
 
 	virtual void animateCursor(int16 cursor) = 0;
+	virtual void updateAnimatedCursor() {}
 	virtual void printTotText(int16 id) = 0;
 	virtual void spriteOperation(int16 operation, bool ttsAddHotspotText = true) = 0;
 
@@ -338,11 +343,21 @@ public:
 
 	void initScreen() override;
 	void animateCursor(int16 cursor) override;
+	void updateAnimatedCursor() override;
 
 
 private:
 	Common::WinResources *_cursors;
 
+	// Animated cursor (.ANI) state
+	Image::AniDecoder *_aniDecoder;
+	uint16 _aniCurrentFrame;
+	uint32 _aniLastFrameTime;
+
+	void clearAniCursor();
+	bool loadAniCursor(Common::SeekableReadStream *stream);
+	bool updateAniCursorFrame();
+
 	bool loadCursorFile();
 	bool loadCursorFromFile(Common::String filename);
 };
diff --git a/engines/gob/draw_v7.cpp b/engines/gob/draw_v7.cpp
index 5876fc26e85..39d14f6f557 100644
--- a/engines/gob/draw_v7.cpp
+++ b/engines/gob/draw_v7.cpp
@@ -30,6 +30,7 @@
 #include "graphics/blit.h"
 #include "graphics/cursorman.h"
 #include "graphics/wincursor.h"
+#include "image/ani.h"
 #include "image/icocur.h"
 
 #include "gob/dataio.h"
@@ -44,13 +45,67 @@
 
 namespace Gob {
 
-Draw_v7::Draw_v7(GobEngine *vm) : Draw_Playtoons(vm), _cursors(nullptr)  {
+Draw_v7::Draw_v7(GobEngine *vm) : Draw_Playtoons(vm), _cursors(nullptr),
+	_aniDecoder(nullptr), _aniCurrentFrame(0), _aniLastFrameTime(0) {
 }
 
 Draw_v7::~Draw_v7() {
+	clearAniCursor();
 	delete _cursors;
 }
 
+void Draw_v7::clearAniCursor() {
+	delete _aniDecoder;
+	_aniDecoder = nullptr;
+	_aniCurrentFrame = 0;
+	_aniLastFrameTime = 0;
+}
+
+bool Draw_v7::loadAniCursor(Common::SeekableReadStream *stream) {
+	clearAniCursor();
+
+	_aniDecoder = new Image::AniDecoder();
+	if (!_aniDecoder->open(*stream, DisposeAfterUse::YES) || _aniDecoder->getMetadata().numFrames == 0) {
+		clearAniCursor();
+		return false;
+	}
+
+	_aniCurrentFrame = 0;
+	_aniLastFrameTime = _vm->_util->getTimeKey();
+
+	return updateAniCursorFrame();
+}
+
+bool Draw_v7::updateAniCursorFrame() {
+	if (!_aniDecoder)
+		return false;
+
+	const Image::AniDecoder::Metadata &meta = _aniDecoder->getMetadata();
+	Image::AniDecoder::FrameDef frameDef = _aniDecoder->getSequenceFrame(_aniCurrentFrame);
+
+	Graphics::Cursor *cursor = nullptr;
+
+	if (meta.isCURFormat) {
+		Common::SeekableReadStream *frameStream = _aniDecoder->openImageStream(frameDef.imageIndex);
+		if (frameStream) {
+			Image::IcoCurDecoder cursorDecoder;
+			cursorDecoder.open(*frameStream);
+			if (cursorDecoder.numItems() > 0)
+				cursor = cursorDecoder.loadItemAsCursor(0);
+			delete frameStream;
+		}
+	}
+
+	if (!cursor)
+		cursor = Graphics::makeDefaultWinCursor();
+
+	CursorMan.replaceCursor(cursor);
+	CursorMan.disableCursorPalette(false);
+	delete cursor;
+
+	return true;
+}
+
 bool Draw_v7::loadCursorFile() {
 	if (_cursors)
 		return true;
@@ -78,23 +133,35 @@ bool Draw_v7::loadCursorFromFile(Common::String cursorName) {
 	const Graphics::Cursor *cursor = nullptr;
 
 	if (cursorName.hasPrefix("*")) {
-		// Load from an external .CUR file
+		// Load from an external .CUR or .ANI file
 		cursorName = cursorName.substr(1);
 		Common::SeekableReadStream *cursorStream = _vm->_dataIO->getFile(cursorName);
 
 		if (cursorStream) {
-			Image::IcoCurDecoder cursorDecoder;
-			cursorDecoder.open(*cursorStream);
-
-			if (cursorDecoder.numItems() > 0) {
-				cursor = cursorDecoder.loadItemAsCursor(0);
+			if (cursorName.hasSuffixIgnoreCase(".ANI")) {
+				// Animated cursor
+				if (loadAniCursor(cursorStream)) {
+					return true;
+				}
+				warning("Failed to open ANI file '%s'", cursorName.c_str());
 			} else {
-				warning("No cursor item found in file '%s'", cursorName.c_str());
+				Image::IcoCurDecoder cursorDecoder;
+				cursorDecoder.open(*cursorStream);
+
+				if (cursorDecoder.numItems() > 0) {
+					cursor = cursorDecoder.loadItemAsCursor(0);
+				} else {
+					warning("No cursor item found in file '%s'", cursorName.c_str());
+				}
+
+				delete cursorStream;
+				clearAniCursor();
 			}
 		} else {
 			warning("External cursor file '%s' not found", cursorName.c_str());
 		}
 	} else {
+		clearAniCursor();
 		// Load from a .DLL cursor file and cursor group
 		if (loadCursorFile())
 			cursorGroup = Graphics::WinCursorGroup::createCursorGroup(_cursors, Common::WinResourceID(cursorName));
@@ -143,6 +210,24 @@ void Draw_v7::initScreen() {
 	_vm->_video->dirtyRectsAll();
 }
 
+void Draw_v7::updateAnimatedCursor() {
+	if (!_aniDecoder)
+		return;
+
+	const Image::AniDecoder::Metadata &metaData = _aniDecoder->getMetadata();
+	if (metaData.numSteps <= 1)
+		return;
+
+	Image::AniDecoder::FrameDef frameDef = _aniDecoder->getSequenceFrame(_aniCurrentFrame);
+	uint32 delayMs = frameDef.delay * 1000 / 60;
+	uint32 now = _vm->_util->getTimeKey();
+	if (now - _aniLastFrameTime >= delayMs) {
+		_aniCurrentFrame = (_aniCurrentFrame + 1) % metaData.numSteps;
+		_aniLastFrameTime = now;
+		updateAniCursorFrame();
+	}
+}
+
 void Draw_v7::animateCursor(int16 cursor) {
 	if (!_cursorSprites)
 		return;
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 5a5346efb12..f73f448c608 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -537,6 +537,8 @@ void VideoPlayer::liveVideosLoop() {
 	if (timeKey - _lastLiveVideosLoopCall < 2)
 		return;
 
+	_vm->_draw->updateAnimatedCursor();
+
 	_lastLiveVideosLoopCall = timeKey;
 
 	for (int slot = 0; slot < kLiveVideoSlotCount; slot++) {




More information about the Scummvm-git-logs mailing list