[Scummvm-git-logs] scummvm master -> 4761d421591c5a64a6f50c9d8dfb0b73a0a046be

neuromancer noreply at scummvm.org
Sat Aug 15 10:20:48 UTC 2026


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

Summary:
f3c5c841eb SCUMM: RA1: added missing persective shifts when moving in some levels
e9751c7286 SCUMM: RA1: fixed some music reset in levels 5 and 7
4761d42159 SCUMM: RA1: fixed axis consistency for joystick for some levels


Commit: f3c5c841ebefe7e92f64ecf9c43b0eb9b228b972
    https://github.com/scummvm/scummvm/commit/f3c5c841ebefe7e92f64ecf9c43b0eb9b228b972
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-15T12:19:11+02:00

Commit Message:
SCUMM: RA1: added missing persective shifts when moving in some levels

Changed paths:
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/smush/rebel/smush_player_ra1.cpp


diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 3dd4d01e138..d87d30cef86 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -153,7 +153,6 @@ public:
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
 	void setCurrentSmushFrame(int32 frame);
 	int getCurrentLevel() const { return _currentLevel; }
-	int getLevelGameplayPhase() const { return _levelGameplayPhase; }
 	uint16 getActiveGameOpcode() const { return _activeGameOpcode; }
 	uint16 getEffectiveGameOpcode() const;
 	uint16 getTargetHitGameOpcode() const;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 3c6e27f1ee1..422c69f601a 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -270,23 +270,13 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 		if (_insane) {
 			InsaneRebel1 *rebel1 = static_cast<InsaneRebel1 *>(_insane);
 			if (rebel1->isInteractiveVideoActive()) {
-				const uint16 gameOp = rebel1->getActiveGameOpcode();
-				const bool fullWidthStoredPatch = (_storedFobjWidth == _vm->_screenWidth);
-				const bool projectedCockpitPatch = (gameOp == 0x0B &&
-					((rebel1->getCurrentLevel() == 4 && rebel1->getLevelGameplayPhase() == 2) ||
-						rebel1->getCurrentLevel() == 7));
-				// Most interactive patches follow the viewport directly; selected
-				// cockpit patches need projected center placement to keep indicators aligned.
-				if (fullWidthStoredPatch || (gameOp == 0x0B && !projectedCockpitPatch) ||
-						gameOp == 0x19 || gameOp == 0x1A) {
-					left += _ra1ViewportOffsetX;
-					top += _ra1ViewportOffsetY;
-				} else {
-					ra1ApplyCenteredFetchPlacement(rebel1, _storedFobjWidth, _storedFobjHeight, left, top);
-					// Convert projected presentation-space placement into the cropped buffer.
-					left += _ra1ViewportOffsetX;
-					top += _ra1ViewportOffsetY;
-				}
+				// DOS routes every FTCH through FUN_28D0A. It sets flag 0x0800
+				// before DispatchFobjCodec, applying quarter-projected placement to
+				// the stored patch regardless of the active GAME opcode or its size.
+				ra1ApplyCenteredFetchPlacement(rebel1, _storedFobjWidth, _storedFobjHeight, left, top);
+				// Convert fixed presentation-space placement into our cropped buffer.
+				left += _ra1ViewportOffsetX;
+				top += _ra1ViewportOffsetY;
 			}
 		}
 


Commit: e9751c728612bdcf9314508b080b2ad4cd979582
    https://github.com/scummvm/scummvm/commit/e9751c728612bdcf9314508b080b2ad4cd979582
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-15T12:19:11+02:00

Commit Message:
SCUMM: RA1: fixed some music reset in levels 5 and 7

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp
    engines/scumm/smush/rebel/smush_player_ra1.h


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index d874fca97a2..24ada67a4ac 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -699,8 +699,11 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 		if (!_vm->_smushVideoShouldFinish &&
 			_pendingRouteCutoverFrame >= 0 &&
 			routeFrame >= (uint32)_pendingRouteCutoverFrame) {
-			if (_player && _currentLevel != 6)
-				_player->setPreserveGameVideoStateOnRelease(true);
+			// L7 destinations can contain no audio chunks at all. L8 preservation
+			// is decided by SmushPlayer at the actual stop, after the final frame
+			// has resolved whether the player or walker was destroyed.
+			if (_currentLevel == 6)
+				preserveInteractiveVideoAudioState();
 			_vm->_smushVideoShouldFinish = true;
 			const int32 resumeFrame = (_currentLevel == 6 && _pendingRouteStartFrame < 0) ?
 				0 : _pendingRouteStartFrame;
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 976ba63c5ca..be44fd47b57 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -562,10 +562,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_interactiveVideoActive = false;
 	_preserveInteractiveRuntimeState = false;
 	_interactiveVideoCheatSkipped = false;
-	_restoreInteractiveVideoAudioState = false;
-	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
-	memset(_savedInteractiveVideoTrackGroupId, 0, sizeof(_savedInteractiveVideoTrackGroupId));
-	_savedInteractiveVideoTrackCount = 0;
 	_gameCounter = 0;
 	_pathBranchEnabled = false;
 	_rightPathSelected = false;
@@ -744,4 +740,8 @@ InsaneRebel1::~InsaneRebel1() {
 	freeSfx();
 }
 
+bool InsaneRebel1::shouldPreserveWalkerRouteOnStop() const {
+	return _vm->_smushVideoShouldFinish && shouldPreserveWalkerRouteVideoState();
+}
+
 } // End of namespace Scumm
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index d87d30cef86..1ccaee5053c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -148,6 +148,11 @@ public:
 	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 		byte *renderBitmap = nullptr, int width = 0, int height = 0);
 	bool isInteractiveVideoActive() const { return _interactiveVideoActive; }
+	bool shouldPreserveWalkerRouteVideoState() const {
+		return _interactiveVideoActive && _currentLevel == 7 && _health >= 0 &&
+			_walkerHealth > 0 && !_interactiveVideoCheatSkipped;
+	}
+	bool shouldPreserveWalkerRouteOnStop() const;
 	// Touch devices use absolute aiming instead of cursor locking.
 	bool isTouchscreenActive() const;
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
@@ -238,7 +243,6 @@ private:
 	void playInteractiveVideo(const char *filename, int32 startFrame = 0);
 	void resetInteractiveVideoAudio();
 	void preserveInteractiveVideoAudioState();
-	void restoreInteractiveVideoAudioState();
 	void setupInteractiveVideoState(int32 startFrame);
 	void resolveSeek(const char *filename, int32 startFrame, int32 &videoOffset, int32 &videoStartFrame);
 	void captureInteractiveVideoInput();
@@ -528,10 +532,6 @@ private:
 
 	// Streamed SMUSH audio.
 	RebelAudio _audio;
-	bool _restoreInteractiveVideoAudioState;
-	int16 _savedInteractiveVideoTrackState[SMUSH_MAX_TRACKS];
-	int _savedInteractiveVideoTrackGroupId[SMUSH_MAX_TRACKS];
-	int _savedInteractiveVideoTrackCount;
 	static const int kNumSfx = 8;
 	enum SfxSlot {
 		kSfxLaserShot = 0,
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index 4ee2a284189..db1f6f364f8 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -908,6 +908,9 @@ void InsaneRebel1::handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame) {
 	if (_player)
 		_player->setPreserveGameVideoStateOnRelease(true);
 
+	// L14PLY2B continues L14PLAY2's audio stream and has no PSAD chunks of
+	// its own, matching the seamless route changes used by levels 7 and 8.
+	preserveInteractiveVideoAudioState();
 	clearFrameObjectPrimaryBits(1, 0x05);
 	clearFrameObjectPrimaryBits(2, 0x40);
 	_vm->_smushVideoShouldFinish = true;
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index a67ae9a8426..86859ec13b2 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -28,7 +28,7 @@
 #include "scumm/file.h"
 #include "scumm/scumm_v7.h"
 #include "scumm/smush/rebel/anim_ra1.h"
-#include "scumm/smush/smush_player.h"
+#include "scumm/smush/rebel/smush_player_ra1.h"
 #include "scumm/insane/rebel1/rebel.h"
 
 namespace Scumm {
@@ -1540,45 +1540,20 @@ void InsaneRebel1::resetInteractiveVideoAudio() {
 
 void InsaneRebel1::preserveInteractiveVideoAudioState() {
 	SmushPlayer *splayer = _vm->_splayer;
-
-	_restoreInteractiveVideoAudioState = false;
-	_savedInteractiveVideoTrackCount = 0;
-	if (!splayer)
-		return;
-
-	_savedInteractiveVideoTrackCount = MIN<int>(splayer->_smushNumTracks, SMUSH_MAX_TRACKS);
-	for (int i = 0; i < _savedInteractiveVideoTrackCount; i++) {
-		_savedInteractiveVideoTrackState[i] = splayer->_smushTracks[i].state;
-		_savedInteractiveVideoTrackGroupId[i] = splayer->_smushTracks[i].groupId;
-	}
-
-	_restoreInteractiveVideoAudioState = true;
-}
-
-void InsaneRebel1::restoreInteractiveVideoAudioState() {
-	if (!_restoreInteractiveVideoAudioState)
-		return;
-
-	_restoreInteractiveVideoAudioState = false;
-	if (_vm->shouldQuit() || _vm->_saveLoadFlag)
-		return;
-
-	SmushPlayer *splayer = _vm->_splayer;
-	if (!splayer)
-		return;
-
-	const int trackCount = MIN<int>(_savedInteractiveVideoTrackCount, splayer->_smushNumTracks);
-	for (int i = 0; i < trackCount; i++) {
-		splayer->_smushTracks[i].state = _savedInteractiveVideoTrackState[i];
-		splayer->_smushTracks[i].groupId = _savedInteractiveVideoTrackGroupId[i];
-	}
+	// RA1's internal route changes are logical stream ends. Taking the existing
+	// EOF exit avoids the shared SMUSH forced-stop reset without changing it.
+	if (splayer)
+		static_cast<SmushPlayerRebel1 *>(splayer)->markLogicalEndOfStream();
 }
 
 void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
+	const bool walkerRouteReplay = (_currentLevel == 7 && _walkerRoundReplay);
 	const bool resumingRoute = startFrame > 0;
-	const bool preserveRuntimeState = _preserveInteractiveRuntimeState || resumingRoute || level7RouteSplice;
-	const bool preserveVideoState = !_preserveInteractiveRuntimeState && resumingRoute && !level7RouteSplice;
+	const bool preserveRuntimeState = _preserveInteractiveRuntimeState || resumingRoute ||
+		level7RouteSplice || walkerRouteReplay;
+	const bool preserveVideoState = walkerRouteReplay ||
+		(!_preserveInteractiveRuntimeState && resumingRoute && !level7RouteSplice);
 
 	SmushPlayer *splayer = _vm->_splayer;
 	_player = splayer;
@@ -1647,7 +1622,8 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 
 void InsaneRebel1::captureInteractiveVideoInput() {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
-	const bool preserveInputState = _preserveInteractiveRuntimeState || level7RouteSplice;
+	const bool walkerRouteReplay = (_currentLevel == 7 && _walkerRoundReplay);
+	const bool preserveInputState = _preserveInteractiveRuntimeState || level7RouteSplice || walkerRouteReplay;
 
 	enableIOSGamepadController();
 
@@ -1681,7 +1657,6 @@ void InsaneRebel1::releaseInteractiveVideoInput() {
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
 	_vm->_splayer->play(filename, 15, videoOffset, videoStartFrame);
 	restoreScreenFlashPalette();
-	restoreInteractiveVideoAudioState();
 	_interactiveVideoActive = false;
 }
 
@@ -1696,8 +1671,10 @@ void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame)
 	_interactiveVideoCheatSkipped = false;
 	int32 videoStartFrame = 0;
 	int32 videoOffset = 0;
+	const bool walkerRouteReplay = (_currentLevel == 7 && _walkerRoundReplay);
 	const bool preserveRuntimeState = _preserveInteractiveRuntimeState ||
-		(startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0);
+		(startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0) ||
+		walkerRouteReplay;
 
 	if (!preserveRuntimeState)
 		resetInteractiveVideoAudio();
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 422c69f601a..b2dae0b0a61 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -217,6 +217,12 @@ void SmushPlayerRebel1::initGameVideoState() {
 }
 
 void SmushPlayerRebel1::releaseGameVideoState() {
+	// Walker routes use a logical stream end at their terminal GAME counter.
+	// Retain the same state if a route instead reaches physical EOF.
+	if (_endOfFile && _insane &&
+			static_cast<InsaneRebel1 *>(_insane)->shouldPreserveWalkerRouteVideoState())
+		return;
+
 	free(_storedFobjData);
 	_storedFobjData = nullptr;
 	_storedFobjDataSize = 0;
@@ -520,6 +526,17 @@ bool SmushPlayerRebel1::handleGameAnimHeader(byte *headerContent) {
 
 void SmushPlayerRebel1::handleGameParseNextFrame() {
 	processDispatches(_smushAudioSampleRate / _speed);
+
+	// The DOS walker loop changes/replays its route without closing the logical
+	// SMUSH stream. Its files request a stop on GAME counter 2629, before physical
+	// EOF. Route this RA1-only continuation through the normal EOF exit, which
+	// leaves audio tracks alive; releaseGameVideoState() retains the STOR object.
+	if (_insane && static_cast<InsaneRebel1 *>(_insane)->shouldPreserveWalkerRouteOnStop())
+		markLogicalEndOfStream();
+}
+
+void SmushPlayerRebel1::markLogicalEndOfStream() {
+	_endOfFile = true;
 }
 
 bool SmushPlayerRebel1::handleGameFrameBufferSelect(int codec, int width, int height) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index d77a9c989d3..fbcdfc05650 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -60,6 +60,7 @@ protected:
 	void handleGameUpdateScreen(const byte *src, int srcPitch, int width, int height) override;
 
 private:
+	void markLogicalEndOfStream();
 	void ra1HandleGost(int32 subSize, Common::SeekableReadStream &b);
 	void ra1HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b);
 	void ra1HandleFade(int32 subSize, Common::SeekableReadStream &b);


Commit: 4761d421591c5a64a6f50c9d8dfb0b73a0a046be
    https://github.com/scummvm/scummvm/commit/4761d421591c5a64a6f50c9d8dfb0b73a0a046be
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-15T12:19:11+02:00

Commit Message:
SCUMM: RA1: fixed axis consistency for joystick for some levels

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 24ada67a4ac..e9d38369534 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -892,14 +892,16 @@ bool InsaneRebel1::updateGamepadReticleAim(int16 &inputX, int16 &inputY, bool *u
 		(_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
 		(_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
 	int dpadY =
-		(_vm->getActionState(kScummActionInsaneUp) ? 1 : 0) -
-		(_vm->getActionState(kScummActionInsaneDown) ? 1 : 0);
+		(_vm->getActionState(kScummActionInsaneDown) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneUp) ? 1 : 0);
 
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
 	const int analogX = CLIP<int32>(((int32)analogAxisX * kRA1CenteredAxisMax) / Common::JOYAXIS_MAX,
 		-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
-	int analogY = CLIP<int32>((-(int32)analogAxisY * kRA1Op0BVerticalAxisMax) / Common::JOYAXIS_MAX,
+	// DOS FUN_231BE leaves joystick-up negative, and the opcode 0x0B handler
+	// (FUN_1CDA7) negates Y when it computes the on-screen flight position.
+	int analogY = CLIP<int32>(((int32)analogAxisY * kRA1Op0BVerticalAxisMax) / Common::JOYAXIS_MAX,
 		-kRA1Op0BVerticalAxisMax, kRA1Op0BVerticalAxisMax);
 
 	if (_optControlsYFlip) {




More information about the Scummvm-git-logs mailing list