[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