[Scummvm-git-logs] scummvm master -> 41ac2b31847622d0662d22c03fe6979e3b43cfbc

neuromancer noreply at scummvm.org
Sat Sep 5 12:15:15 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:
ee1506a178 SCUMM: RA2: text parser now preserves intentionally empty entries
41ac2b3184 SCUMM: RA2: fixed corrupted frames in L13


Commit: ee1506a178e3c435ecb6ebc7d477ecac844f3fee
    https://github.com/scummvm/scummvm/commit/ee1506a178e3c435ecb6ebc7d477ecac844f3fee
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-05T14:02:17+02:00

Commit Message:
SCUMM: RA2: text parser now preserves intentionally empty entries

Changed paths:
    engines/scumm/smush/rebel/smush_player_ra2.cpp


diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 8343288cef9..b23dd943e87 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -543,20 +543,21 @@ public:
 				data_start++;
 
 			char *data_end = data_start;
-			while (1) {
+			while (data_end < buffer + length) {
 				if (data_end[-2] == '\r' && data_end[-1] == '\n' && data_end[0] == '\r' && data_end[1] == '\n') break;
 				if (data_end[-2] == '\n' && data_end[-1] == '\n') break;
 				if (data_end[-2] == '\r' && data_end[-1] == '\n' && data_end[0] == '#') break;
 				data_end++;
-				if (data_end >= buffer + length) { data_end = buffer + length; break; }
 			}
 			data_end -= 2;
 
-			if (data_end <= data_start) { def_start = strchr(def_end + 1, '#'); continue; }
+			// Retail TRS_DEMO_TEXT is defined but empty. Keep its entry so the
+			// intro's final TRES cue does not resolve to "unknown string".
+			if (data_end < data_start)
+				data_end = data_start;
 
-			if (data_start[0] == '/' && data_start[1] == '/')
+			if (data_end - data_start >= 2 && data_start[0] == '/' && data_start[1] == '/')
 				data_start += 2;
-			if (data_end <= data_start) { def_start = strchr(def_end + 1, '#'); continue; }
 
 			char *value = new char[data_end - data_start + 1];
 			memcpy(value, data_start, data_end - data_start);
@@ -581,7 +582,7 @@ public:
 			} else {
 				delete[] value;
 			}
-			def_start = strchr(data_end + 2, '#');
+			def_start = strchr(data_end, '#');
 		}
 		return true;
 	}


Commit: 41ac2b31847622d0662d22c03fe6979e3b43cfbc
    https://github.com/scummvm/scummvm/commit/41ac2b31847622d0662d22c03fe6979e3b43cfbc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-05T14:02:17+02:00

Commit Message:
SCUMM: RA2: fixed corrupted frames in L13

Changed paths:
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h


diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 9fde6727c1b..ab2c7d0c858 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2476,7 +2476,7 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	// End the looping attack-run segment once the shield/reactor is destroyed.
 	if (_rebelShieldGateActive) {
 		// Level 13: the finale (continuation segment, flag 0x40) ends when the last armed
-		// group (the reactor) is depleted; the approach segment plays fully.
+		// group (the reactor) is depleted.
 		if (_rebelReactorMode && _rebelGaugeArmed && _rebelLastArmedSlot >= 0 &&
 		    (_player->_curVideoFlags & 0x40) != 0) {
 			const int slot = _rebelLastArmedSlot;
@@ -2486,6 +2486,15 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		}
 		if (_rebelShieldDestroyed)
 			_vm->_smushVideoShouldFinish = true;
+
+		// The original level 13 switches at frame count - 10. The remaining
+		// nine frames overlap the cached LOAD bridge that precedes 13PLAY_B.
+		if (_rebelReactorMode && maxFrame >= 9 && curFrame == maxFrame - 9 &&
+				!static_cast<SmushPlayerRebel2 *>(_player)->isPlayingLoadBuffer() &&
+				!_vm->_smushVideoShouldFinish) {
+			// Use normal EOF handling so queued audio survives the handoff.
+			_player->_endOfFile = true;
+		}
 	}
 
 	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index b23dd943e87..a0fd008b0c0 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -21,6 +21,7 @@
 
 #include "common/config-manager.h"
 #include "common/endian.h"
+#include "common/memstream.h"
 #include "common/rect.h"
 #include "common/system.h"
 
@@ -158,6 +159,10 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_loadReadOffset = 8;
 	_lastLoadChunkIdx = -1;
 	_loadStreamId = 0;
+	_loadPlaybackPending = false;
+	_loadContinuationStream = nullptr;
+	_loadContinuationSize = 0;
+	_loadContinuationFrameCount = 0;
 	_ra2FrameSourceSkipX = 0;
 	_ra2FrameSourceSkipY = 0;
 	_ra2FrameObjectOriginalWidth = 0;
@@ -182,6 +187,8 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 }
 
 void SmushPlayerRebel2::destroyGamePlayerFields() {
+	delete _loadContinuationStream;
+	_loadContinuationStream = nullptr;
 	delete _multiFont;
 	_multiFont = nullptr;
 	free(_storedFobjData);
@@ -213,6 +220,7 @@ void SmushPlayerRebel2::ra2InitAudioTrackSizes() {
 }
 
 void SmushPlayerRebel2::initGameVideoState() {
+	_loadPlaybackPending = (_curVideoFlags & 0x40) != 0;
 	_ra2PendingAnimHeaderPalette = false;
 	_ra2UsingGameplaySurface = false;
 	_smushAudioTable[100] = 0;
@@ -232,6 +240,9 @@ void SmushPlayerRebel2::initGameVideoState() {
 }
 
 void SmushPlayerRebel2::releaseGameVideoState() {
+	delete _loadContinuationStream;
+	_loadContinuationStream = nullptr;
+	_loadPlaybackPending = false;
 	free(_lastFobjData);
 	_lastFobjData = nullptr;
 	_lastFobjDataSize = 0;
@@ -1245,9 +1256,59 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 }
 
 void SmushPlayerRebel2::handleGameParseNextFrame() {
+	if (_loadPlaybackPending) {
+		_loadPlaybackPending = false;
+		ra2StartLoadPlayback();
+	} else if (_loadContinuationStream && _base->pos() >= _baseSize) {
+		delete _base;
+		_base = _loadContinuationStream;
+		_baseSize = _loadContinuationSize;
+		_nbframes = _loadContinuationFrameCount;
+		_loadContinuationStream = nullptr;
+		_frame = 0;
+		_startFrame = 0;
+		_startTime = _vm->_system->getMillis();
+		_pauseTime = 0;
+	}
 	processDispatches(_smushAudioSampleRate / 12);
 }
 
+void SmushPlayerRebel2::ra2StartLoadPlayback() {
+	// Continuation movies omit the opening frames stored in the preceding
+	// movie's LOAD chunks. Play these first, including their keyframe, and
+	// retain the decoder when returning to the continuation on disk.
+	if (!_loadBuffer || _loadBufferOffset < 22 ||
+			READ_BE_UINT32(_loadBuffer) != MKTAG('A', 'N', 'I', 'M') ||
+			READ_BE_UINT32(_loadBuffer + 8) != MKTAG('A', 'H', 'D', 'R'))
+		return;
+
+	const uint32 animSize = READ_BE_UINT32(_loadBuffer + 4);
+	const uint32 headerSize = READ_BE_UINT32(_loadBuffer + 12);
+	if (animSize > (uint32)_loadBufferOffset - 8 || animSize < 8 ||
+			headerSize < 0x306 || headerSize > animSize - 8) {
+		warning("SmushPlayerRebel2::ra2StartLoadPlayback: incomplete LOAD animation");
+		return;
+	}
+
+	const uint32 frameOffset = 16 + headerSize;
+	const uint16 frameCount = READ_LE_UINT16(_loadBuffer + 18);
+	if (frameCount == 0 || frameOffset >= animSize + 8)
+		return;
+
+	Common::MemoryReadStream loaded(_loadBuffer, animSize + 8);
+	loaded.seek(frameOffset);
+	Common::SeekableReadStream *frames = loaded.readStream(animSize + 8 - frameOffset);
+
+	// The disk AHDR has already been consumed. Keep its palette and resume
+	// at its first FRME after the cached animation, without releasing SMUSH.
+	_loadContinuationStream = _base;
+	_loadContinuationSize = _baseSize;
+	_loadContinuationFrameCount = _nbframes;
+	_base = frames;
+	_baseSize = frames->size();
+	_nbframes = frameCount;
+}
+
 bool SmushPlayerRebel2::handleGameFrameBufferSelect(int codec, int width, int height) {
 	if ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)) {
 		return ra2SelectFrameBuffer(codec, width, height);
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 28036038f3c..3c8b37568ce 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -32,6 +32,7 @@ public:
 	~SmushPlayerRebel2() override;
 	bool ra2PromoteCurrentFrameToHiRes(int scrollX, int scrollY);
 	bool ra2PromoteHandler7PerspectiveToHiRes(int perspectiveX, int perspectiveY, int viewShift);
+	bool isPlayingLoadBuffer() const { return _loadContinuationStream != nullptr; }
 
 protected:
 	void initGamePlayerFields() override;
@@ -68,6 +69,7 @@ protected:
 
 private:
 	void handleLoad(int32 subSize, Common::SeekableReadStream &b);
+	void ra2StartLoadPlayback();
 	void ra2HandleTextResource(const char *str, int fontId, int color,
 							   int pos_x, int pos_y, int left, int top,
 							   int width, int height, TextStyleFlags flg);
@@ -94,6 +96,10 @@ private:
 	int32 _loadReadOffset;
 	int16 _lastLoadChunkIdx;
 	int16 _loadStreamId;
+	bool _loadPlaybackPending;
+	Common::SeekableReadStream *_loadContinuationStream;
+	uint32 _loadContinuationSize;
+	int32 _loadContinuationFrameCount;
 	int _ra2FrameSourceSkipX;
 	int _ra2FrameSourceSkipY;
 	int _ra2FrameObjectOriginalWidth;




More information about the Scummvm-git-logs mailing list