[Scummvm-git-logs] scummvm master -> ee8a663ea20b92fdb3ae1fbef457e8998cb4c60b

neuromancer noreply at scummvm.org
Sat Jun 6 11:26:25 UTC 2026


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

Summary:
3d1b55ea84 SCUMM: RA1: accept 3DO passwords
b9c82d2942 SCUMM: RA1: adapt on-foot controls using 3DO controls
10adbd8f94 SCUMM: RA1: disable useless warnings
adc9613698 SCUMM: RA1: fixed some missing voices
ee8a663ea2 SCUMM: RA1: added support for virtual gamepad in ios


Commit: 3d1b55ea84758edc0ca136f4f0f2736cb7985217
    https://github.com/scummvm/scummvm/commit/3d1b55ea84758edc0ca136f4f0f2736cb7985217
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T13:26:09+02:00

Commit Message:
SCUMM: RA1: accept 3DO passwords

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index a6ac4ebd4bc..a3752286d10 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -163,6 +163,39 @@ static int getRebel1PasscodeStartLevel(int passwordIndex) {
 	}
 }
 
+// 3DO Launchme_ARMv4 FUN_000092c4 compares the entered passcode against 45
+// XOR-0xAA encoded 20-byte slots at 0x262FC. The caller's Ghidra disassembly at
+// 0x12A58 maps those slots in three-code difficulty groups to the next chapter.
+const char *const kRebel1ThreeDOPasswords[] = {
+	"BOSSK", "BOTHAN", "BORDOK",
+	"ENGRET", "HERGLIC", "SKYNX",
+	"RALRRA", "LEENI", "DEFEL",
+	"FRIJA", "THRAWN", "JEDGAR",
+	"LAFRA", "LWYLL", "MADINE",
+	"DERLIN", "MAZZIC", "TARKIN",
+	"MOLTOK", "JULPA", "MOTHMA",
+	"MORAG", "MORRT", "GLAYYD",
+	"TANTISS", "MUFTAK", "OTTEGA",
+	"OSWAFL", "RASKAR", "RISHII",
+	"KLAATU", "JHOFF", "IZRINA",
+	"IRENEZ", "ITHOR", "KARRDE",
+	"LIANNA", "UMWAK", "VONZEL",
+	"PAKKA", "ORLOK", "OSSUS",
+	"NORVAL", "NKLLON", "MALANI"
+};
+
+int getRebel1ThreeDOPasscodeDifficulty(int passwordIndex) {
+	return (passwordIndex - 1) % 3;
+}
+
+int getRebel1ThreeDOPasscodeStartLevel(int passwordIndex) {
+	const int group = (passwordIndex - 1) / 3;
+	if (group < 0 || group > 14)
+		return 0;
+
+	return group == 14 ? kRA1NumLevels + 1 : group + 2;
+}
+
 static char normalizeRebel1PasscodeChar(char c) {
 	if (c >= 'a' && c <= 'z')
 		return c - ('a' - 'A');
@@ -1238,6 +1271,22 @@ int InsaneRebel1::runPasscodeEntryDialog() {
 		}
 	}
 
+	for (int i = 1; i <= (int)ARRAYSIZE(kRebel1ThreeDOPasswords); i++) {
+		const char *password = kRebel1ThreeDOPasswords[i - 1];
+		if (!scumm_stricmp(_textEntryBuffer, password)) {
+			const int targetLevel = getRebel1ThreeDOPasscodeStartLevel(i);
+			if (targetLevel == 0)
+				return 0;
+
+			_difficulty = getRebel1ThreeDOPasscodeDifficulty(i);
+			if (targetLevel <= kRA1NumLevels)
+				_startLevel = targetLevel;
+			debugC(DEBUG_INSANE, "RA1 3DO passcode accepted: slot=%d password=%s difficulty=%d target=%d",
+				i, password, _difficulty, targetLevel);
+			return targetLevel;
+		}
+	}
+
 	debugC(DEBUG_INSANE, "RA1 passcode rejected: '%s'", _textEntryBuffer);
 	return 0;
 }


Commit: b9c82d294254c9bb3c878e27bb88c7ae3b8a478c
    https://github.com/scummvm/scummvm/commit/b9c82d294254c9bb3c878e27bb88c7ae3b8a478c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T13:26:09+02:00

Commit Message:
SCUMM: RA1: adapt on-foot controls using 3DO controls

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index f4c68f3f53d..41232a893bd 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1886,7 +1886,7 @@ void InsaneRebel1::updateOnFootSequence() {
 		// Right edge: snap to center, step character right
 		_shipDirIndex = 15;
 		_onFootCharX += 0x3A;
-	} else if (_onFootAnimCounter < 5 && !(_playerFired && _fireCooldown == 0)) {
+	} else if (_onFootAnimCounter < 5 && !_playerSecondaryHeld) {
 		// Original calls QuantizeDirection8Way with the cursor and character
 		// center, but the DOS on-foot axis is mirrored relative to the screen
 		// coordinates used by this port. Use the visual screen-space vector so
@@ -1897,12 +1897,13 @@ void InsaneRebel1::updateOnFootSequence() {
 			(int16)ra1ShotDirection(centerX, centerY, _shipPosX, _shipPosY), -4, 4);
 		_shipDirIndex = aimDir + 15;
 	} else {
-		// Walking based on mouse input direction
+		// Walking based on input direction. The 3DO second held button skips the
+		// early aim-pose branch above and reaches these walk tests immediately.
 		int16 inputX = 0, inputY = 0;
 		preprocessMouseAxes(inputX, inputY);
-		if (inputX > 0x1E && _onFootCharX < 0x72)
+		if (inputX > 0x1E && _onFootCharX < 0x73)
 			_shipDirIndex = 6;  // Walk right
-		else if (inputX < -0x1E && _onFootCharX > -0x72)
+		else if (inputX < -0x1E && _onFootCharX > -0x73)
 			_shipDirIndex = 4;  // Walk left
 	}
 
@@ -2014,6 +2015,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	resetGamepadReticleAim();
 
 	_playerFired = false;
+	_playerSecondaryHeld = false;
 	_fireCooldown = 0;
 	_rapidFirePhase = 0;
 	memset(_shotSlots, 0, sizeof(_shotSlots));
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index a3752286d10..4d34c9a4094 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -830,6 +830,9 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			_activeInputSource = kInputSourceJoystickDigital;
 		}
 
+		if (!pressed && event.customType == kScummActionInsaneSwitch)
+			_playerSecondaryHeld = false;
+
 		if (event.customType == kScummActionInsaneBack) {
 			if (!pressed)
 				return true;
@@ -881,6 +884,11 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			_playerFired = pressed;
 			return true;
 		}
+
+		if (_interactiveVideoActive && !_menuActive && event.customType == kScummActionInsaneSwitch) {
+			_playerSecondaryHeld = pressed;
+			return true;
+		}
 	}
 
 	if (_menuActive && _textEntryActive && event.type == Common::EVENT_KEYDOWN)
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index eb41ddadbe6..2a90f61f59e 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -382,6 +382,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 
 	// Shooting/targeting state
 	_playerFired = false;
+	_playerSecondaryHeld = false;
 	_fireCooldown = 0;
 	_rapidFirePhase = 0;
 	_gameplayFlags75fe = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 6a0ad752c99..0fb7a7e1ebe 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -617,6 +617,8 @@ private:
 
 	// Shooting state — FUN_1CCA0 (0x1CCA0)
 	bool _playerFired;       // 0x7570: current fire-button state
+	// 3DO ControlB/second held button used by L9 on-foot controls.
+	bool _playerSecondaryHeld;
 	int16 _fireCooldown;     // 0x757C: previous-frame fire-button state (edge gate when rapid fire is off)
 	int16 _rapidFirePhase;   // 3DO FUN_0000c3a4: held-fire modulo-3 shot gate
 	uint16 _gameplayFlags75fe; // 0x75FE: gameplay mode flags
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 7a059404df5..1bd84c7d668 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1183,14 +1183,14 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->addDefaultInputMapping("JOY_A");
 		rebel1Keymap->addAction(act);
 
-		act = new Action("RA1CANCEL", _("Menu back"));
+		act = new Action("RA1CANCEL", _("Walk / menu back"));
 		act->setCustomEngineActionEvent(kScummActionInsaneSwitch);
-		act->addDefaultInputMapping("JOY_X");
+		act->addDefaultInputMapping("JOY_B");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1SKIP", _("Skip / menu back"));
 		act->setCustomEngineActionEvent(kScummActionInsaneSkip);
-		act->addDefaultInputMapping("JOY_B");
+		act->addDefaultInputMapping("JOY_X");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1BACK", _("Menu"));


Commit: 10adbd8f946a2fa8cc93eda2d3b26cb2684edfa9
    https://github.com/scummvm/scummvm/commit/10adbd8f946a2fa8cc93eda2d3b26cb2684edfa9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T13:26:09+02:00

Commit Message:
SCUMM: RA1: disable useless warnings

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


diff --git a/engines/scumm/insane/rebel1/levels.cpp b/engines/scumm/insane/rebel1/levels.cpp
index b24fb5b85d0..aeedbc9e76b 100644
--- a/engines/scumm/insane/rebel1/levels.cpp
+++ b/engines/scumm/insane/rebel1/levels.cpp
@@ -43,13 +43,14 @@ void resetSpriteBank(RA1SpriteBank &bank) {
 // RA1 NUTs can have odd-size FOBJ chunks padded to 2-byte alignment within
 // FRME containers. This loader handles that padding properly, unlike the
 // shared NutRenderer::loadFont which assumes even-size chunks.
-bool InsaneRebel1::loadRA1Nut(const char *filename, RA1SpriteBank &bank) {
+bool InsaneRebel1::loadRA1Nut(const char *filename, RA1SpriteBank &bank, bool warnIfMissing) {
 	resetSpriteBank(bank);
 
 	ScummFile *file = _vm->instantiateScummFile();
 	_vm->openFile(*file, filename);
 	if (!file->isOpen()) {
-		warning("InsaneRebel1::loadRA1Nut: can't open %s", filename);
+		if (warnIfMissing)
+			warning("InsaneRebel1::loadRA1Nut: can't open %s", filename);
 		delete file;
 		return false;
 	}
@@ -164,18 +165,18 @@ bool InsaneRebel1::loadRA1Nut(const char *filename, RA1SpriteBank &bank) {
 void InsaneRebel1::loadLevelSprites(int level) {
 	// Ship/character direction bank — try BANK1, BANK, then PILOT (Level 9 on-foot)
 	Common::String bankFile = Common::String::format("LVL%d/L%dBANK1.NUT", level, level);
-	if (!loadRA1Nut(bankFile.c_str(), _shipBank)) {
+	if (!loadRA1Nut(bankFile.c_str(), _shipBank, false)) {
 		Common::String legacyBankFile = Common::String::format("LVL%d/L%dBANK.NUT", level, level);
-		if (!loadRA1Nut(legacyBankFile.c_str(), _shipBank)) {
+		if (!loadRA1Nut(legacyBankFile.c_str(), _shipBank, false)) {
 			Common::String pilotFile = Common::String::format("LVL%d/L%dPILOT.NUT", level, level);
-			if (!loadRA1Nut(pilotFile.c_str(), _shipBank))
+			if (!loadRA1Nut(pilotFile.c_str(), _shipBank, false))
 				debugC(DEBUG_INSANE, "InsaneRebel1::loadLevelSprites: No BANK1/BANK/PILOT for level %d", level);
 		}
 	}
 
 	// Secondary ship bank used by some level-specific handlers (e.g. LVL1 mode-2).
 	Common::String bankFileAlt = Common::String::format("LVL%d/L%dBANK2.NUT", level, level);
-	if (!loadRA1Nut(bankFileAlt.c_str(), _shipBankAlt)) {
+	if (!loadRA1Nut(bankFileAlt.c_str(), _shipBankAlt, false)) {
 		debugC(DEBUG_INSANE, "InsaneRebel1::loadLevelSprites: No BANK2 for level %d", level);
 	}
 
@@ -183,9 +184,10 @@ void InsaneRebel1::loadLevelSprites(int level) {
 
 	// Explosion sprites — try BANG first, then EXPLD
 	Common::String bangFile = Common::String::format("LVL%d/L%dBANG.NUT", level, level);
-	if (!loadRA1Nut(bangFile.c_str(), _bangBank)) {
+	if (!loadRA1Nut(bangFile.c_str(), _bangBank, false)) {
 		Common::String expldFile = Common::String::format("LVL%d/L%dEXPLD.NUT", level, level);
-		loadRA1Nut(expldFile.c_str(), _bangBank);
+		if (!loadRA1Nut(expldFile.c_str(), _bangBank, false))
+			debugC(DEBUG_INSANE, "InsaneRebel1::loadLevelSprites: No BANG/EXPLD for level %d", level);
 	}
 
 	// Laser/shot effect sprites
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 0fb7a7e1ebe..d34ec43f0d1 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -210,7 +210,7 @@ private:
 	void captureInteractiveVideoInput();
 	void releaseInteractiveVideoInput();
 	void playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame);
-	bool loadRA1Nut(const char *filename, RA1SpriteBank &bank);
+	bool loadRA1Nut(const char *filename, RA1SpriteBank &bank, bool warnIfMissing = true);
 	void loadLevelSprites(int level);
 	void handleGameOpcode5EReset(uint32 param1);
 	void handleGameOpcode5DLinkLatch(uint32 param1);


Commit: adc961369862b6b05ac7c02939438361280fdd16
    https://github.com/scummvm/scummvm/commit/adc961369862b6b05ac7c02939438361280fdd16
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T13:26:09+02:00

Commit Message:
SCUMM: RA1: fixed some missing voices

Changed paths:
    engines/scumm/insane/rebel/rebel_audio.cpp
    engines/scumm/insane/rebel/rebel_audio.h
    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/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index 6c1f85d4bf2..b7856bcc978 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -45,7 +45,7 @@ void RebelAudio::init(ScummEngine_v7 *vm, int sampleRate) {
 	}
 }
 
-void RebelAudio::terminate() {
+void RebelAudio::reset() {
 	if (!_vm)
 		return;
 
@@ -56,11 +56,16 @@ void RebelAudio::terminate() {
 		}
 		if (_streams[i]) {
 			_streams[i]->finish();
+			delete _streams[i];
 			_streams[i] = nullptr;
 		}
 	}
 }
 
+void RebelAudio::terminate() {
+	reset();
+}
+
 void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate) {
 	if (!_vm || trackIdx < 0 || trackIdx >= kMaxTracks || size <= 0 || !data)
 		return;
diff --git a/engines/scumm/insane/rebel/rebel_audio.h b/engines/scumm/insane/rebel/rebel_audio.h
index a91ef4f28dc..4b13efe5e06 100644
--- a/engines/scumm/insane/rebel/rebel_audio.h
+++ b/engines/scumm/insane/rebel/rebel_audio.h
@@ -39,6 +39,7 @@ public:
 	RebelAudio();
 
 	void init(ScummEngine_v7 *vm, int sampleRate);
+	void reset();
 	void terminate();
 	int sampleRate() const { return _sampleRate; }
 
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 2bd976587ad..5cdd1704d49 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -155,6 +155,11 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	SmushPlayer *splayer = _vm->_splayer;
 	_player = splayer;
 	restoreScreenFlashPalette();
+	// DOS PlayFrontendAnmAndWait keeps pumping until frontend audio clears.
+	// ScummVM's Rebel queues outlive SmushPlayer::play(), so clear stale
+	// passive-cinematic audio before chaining the next ANM.
+	_audio.reset();
+	splayer->resetAudioTracks();
 	_interactiveVideoActive = false;
 	_vm->_smushVideoShouldFinish = false;
 	splayer->setCurVideoFlags(0x420);
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index d6208e7ad8c..c2a9fa8bdbb 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -149,6 +149,7 @@ void SmushPlayerRebel1::initGamePlayerFields() {
 	_ra1ViewportOffsetX = 0;
 	_ra1ViewportOffsetY = 0;
 	_ra1FrameSourceSkipY = 0;
+	_ra1LastFrameObjectVisible = true;
 	_ra1FadeFrame = nullptr;
 	_ra1FadeFrameSize = 0;
 	_ra1FadeFrameWidth = 0;
@@ -184,6 +185,7 @@ void SmushPlayerRebel1::resetGameVideoState() {
 	_ra1ObjOverlayHeight = 0;
 	_ra1ViewportOffsetX = 0;
 	_ra1ViewportOffsetY = 0;
+	_ra1LastFrameObjectVisible = true;
 	_ra1UseFadeFrame = false;
 }
 
@@ -649,6 +651,7 @@ void SmushPlayerRebel1::handleFrameObject(int32 subSize, Common::SeekableReadStr
 	assert(subSize >= 14);
 	if (_skipNext) {
 		_skipNext = false;
+		_ra1LastFrameObjectVisible = false;
 		return;
 	}
 
@@ -699,11 +702,13 @@ void SmushPlayerRebel1::handleFrameObject(int32 subSize, Common::SeekableReadStr
 		InsaneRebel1 *rebel1 = static_cast<InsaneRebel1 *>(_insane);
 		if (!rebel1->handleFrameObjectTarget((int16)ra1ObjectId, (int16)rawLeft, (int16)rawTop,
 				(int16)width, (int16)height, codec, ra1Param)) {
+			_ra1LastFrameObjectVisible = false;
 			free(chunk_buffer);
 			return;
 		}
 	}
 
+	_ra1LastFrameObjectVisible = true;
 	decodeFrameObject(codec, chunk_buffer, left, top, width, height, chunk_size, ra1Param, ra1Parm2);
 	free(chunk_buffer);
 }
@@ -850,6 +855,10 @@ bool SmushPlayerRebel1::ra1DispatchFrameChunk(uint32 subType, int32 subSize, int
 	case MKTAG('P','V','O','C'):
 		ra1HandleFrameAudioChunk(subSize, b);
 		break;
+	case MKTAG('P','S','D','2'):
+		if (_ra1LastFrameObjectVisible)
+			ra1HandleFrameAudioChunk(subSize, b);
+		break;
 	case MKTAG('T','R','E','S'):
 	case MKTAG('T','E','X','T'):
 		handleTextResource(subType, subSize, b);
@@ -888,7 +897,6 @@ bool SmushPlayerRebel1::ra1DispatchFrameChunk(uint32 subType, int32 subSize, int
 	case MKTAG('A','D','L','2'):
 	case MKTAG('S','B','L',' '):
 	case MKTAG('S','B','L','2'):
-	case MKTAG('P','S','D','2'):
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel1::handleFrame: skipping chunk %s (%d bytes)", tag2str(subType), subSize);
 		break;
 	default:
@@ -905,6 +913,7 @@ bool SmushPlayerRebel1::ra1DispatchFrameChunk(uint32 subType, int32 subSize, int
 void SmushPlayerRebel1::handleFrame(int32 frameSize, Common::SeekableReadStream &b) {
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel1::handleFrame(%d)", _frame);
 	_skipNext = false;
+	_ra1LastFrameObjectVisible = true;
 	handleGameFrameStart();
 	const bool fastForwarding = isFastForwardingCurrentFrame();
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index be70db7b950..95ec9485179 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -94,6 +94,7 @@ private:
 	int _ra1ViewportOffsetX;
 	int _ra1ViewportOffsetY;
 	int _ra1FrameSourceSkipY;
+	bool _ra1LastFrameObjectVisible;
 
 	// RA1 FADE chunks update the visible 320x200 screen through a sparse
 	// copy mask, separate from the decoded frame buffer.


Commit: ee8a663ea20b92fdb3ae1fbef457e8998cb4c60b
    https://github.com/scummvm/scummvm/commit/ee8a663ea20b92fdb3ae1fbef457e8998cb4c60b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T13:26:09+02:00

Commit Message:
SCUMM: RA1: added support for virtual gamepad in ios

Changed paths:
  A engines/scumm/insane/rebel/rebel_gamepad.cpp
  A engines/scumm/insane/rebel/rebel_gamepad.h
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/module.mk


diff --git a/engines/scumm/insane/rebel/rebel_gamepad.cpp b/engines/scumm/insane/rebel/rebel_gamepad.cpp
new file mode 100644
index 00000000000..e25e3e74be4
--- /dev/null
+++ b/engines/scumm/insane/rebel/rebel_gamepad.cpp
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "scumm/insane/rebel/rebel_gamepad.h"
+
+namespace Scumm {
+
+namespace {
+
+#ifdef IPHONE
+const char *const kIOSGamepadControllerKey = "gamepad_controller";
+const char *const kIOSGamepadControllerMinimalLayoutKey = "gamepad_controller_minimal_layout";
+const char *const kIOSGamepadControllerDirectionalInputKey = "gamepad_controller_directional_input";
+const int kIOSGamepadControllerDirectionalInputDpad = 1;
+
+void saveDomainSetting(RebelIOSGamepadControllerState::SavedSetting &setting,
+		const Common::ConfigManager::Domain *domain, const char *key) {
+	setting.present = domain && domain->contains(key);
+	setting.value = setting.present ? domain->getVal(key) : Common::String();
+}
+
+void restoreDomainSetting(const RebelIOSGamepadControllerState::SavedSetting &setting,
+		const Common::String &domainName, const char *key) {
+	Common::ConfigManager::Domain *domain = ConfMan.getDomain(domainName);
+	if (!domain)
+		return;
+
+	if (setting.present)
+		ConfMan.set(key, setting.value, domainName);
+	else if (domain->contains(key))
+		ConfMan.removeKey(key, domainName);
+}
+#endif
+
+void clearSavedSetting(RebelIOSGamepadControllerState::SavedSetting &setting) {
+	setting.present = false;
+	setting.value.clear();
+}
+
+} // End of anonymous namespace
+
+RebelIOSGamepadControllerState::RebelIOSGamepadControllerState() :
+		_active(false),
+		_gamepadController(),
+		_gamepadControllerMinimalLayout(),
+		_gamepadControllerDirectionalInput() {
+	clearSavedSetting(_gamepadController);
+	clearSavedSetting(_gamepadControllerMinimalLayout);
+	clearSavedSetting(_gamepadControllerDirectionalInput);
+}
+
+void RebelIOSGamepadControllerState::enable() {
+#ifdef IPHONE
+	if (_active)
+		return;
+
+	_domainName = ConfMan.getActiveDomainName();
+	if (_domainName.empty())
+		return;
+
+	const Common::ConfigManager::Domain *domain = ConfMan.getDomain(_domainName);
+	saveDomainSetting(_gamepadController, domain, kIOSGamepadControllerKey);
+	saveDomainSetting(_gamepadControllerMinimalLayout, domain, kIOSGamepadControllerMinimalLayoutKey);
+	saveDomainSetting(_gamepadControllerDirectionalInput, domain, kIOSGamepadControllerDirectionalInputKey);
+
+	// Same iOS virtual-controller profile used by Freescape: compact d-pad overlay.
+	ConfMan.setBool(kIOSGamepadControllerKey, true, _domainName);
+	ConfMan.setBool(kIOSGamepadControllerMinimalLayoutKey, true, _domainName);
+	ConfMan.setInt(kIOSGamepadControllerDirectionalInputKey, kIOSGamepadControllerDirectionalInputDpad, _domainName);
+	g_system->applyBackendSettings();
+	_active = true;
+#endif
+}
+
+void RebelIOSGamepadControllerState::restore() {
+#ifdef IPHONE
+	if (!_active)
+		return;
+
+	restoreDomainSetting(_gamepadController, _domainName, kIOSGamepadControllerKey);
+	restoreDomainSetting(_gamepadControllerMinimalLayout, _domainName, kIOSGamepadControllerMinimalLayoutKey);
+	restoreDomainSetting(_gamepadControllerDirectionalInput, _domainName, kIOSGamepadControllerDirectionalInputKey);
+	g_system->applyBackendSettings();
+
+	clearSavedSetting(_gamepadController);
+	clearSavedSetting(_gamepadControllerMinimalLayout);
+	clearSavedSetting(_gamepadControllerDirectionalInput);
+	_domainName.clear();
+	_active = false;
+#endif
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/insane/rebel/rebel_gamepad.h b/engines/scumm/insane/rebel/rebel_gamepad.h
new file mode 100644
index 00000000000..3ecca799a42
--- /dev/null
+++ b/engines/scumm/insane/rebel/rebel_gamepad.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_INSANE_REBEL_GAMEPAD_H
+#define SCUMM_INSANE_REBEL_GAMEPAD_H
+
+#include "common/str.h"
+
+namespace Scumm {
+
+class RebelIOSGamepadControllerState {
+public:
+	struct SavedSetting {
+		bool present;
+		Common::String value;
+	};
+
+	RebelIOSGamepadControllerState();
+
+	void enable();
+	void restore();
+
+	bool isEnabled() const { return _active; }
+
+private:
+	bool _active;
+	Common::String _domainName;
+	SavedSetting _gamepadController;
+	SavedSetting _gamepadControllerMinimalLayout;
+	SavedSetting _gamepadControllerDirectionalInput;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 4d34c9a4094..7a67f39b3ac 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -593,6 +593,24 @@ bool InsaneRebel1::handleControllerMenuAxis(int16 oldAxisX, int16 oldAxisY) {
 	return false;
 }
 
+void InsaneRebel1::openGameplayMainMenu() {
+	if (!_player)
+		return;
+
+	const bool wasPaused = _player->_paused;
+	if (!wasPaused)
+		_player->pause();
+
+	const bool restoreGamepad = _iosGamepadControllerState.isEnabled();
+	restoreIOSGamepadController();
+	_vm->openMainMenuDialog();
+	if (restoreGamepad && _interactiveVideoActive && !_menuActive && !_vm->shouldQuit())
+		enableIOSGamepadController();
+
+	if (!wasPaused)
+		_player->unpause();
+}
+
 // ScummVM-exclusive feature (not in the original game): let the player navigate and
 // activate the front-end menus with the mouse. Hovering highlights an item and a left
 // click activates it (same as pressing accept). The item hit-rectangles mirror the
@@ -841,12 +859,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				return true;
 			}
 			if (_player) {
-				const bool wasPaused = _player->_paused;
-				if (!wasPaused)
-					_player->pause();
-				_vm->openMainMenuDialog();
-				if (!wasPaused)
-					_player->unpause();
+				openGameplayMainMenu();
 			}
 			return true;
 		}
@@ -942,12 +955,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			}
 
 			if (_player) {
-				const bool wasPaused = _player->_paused;
-				if (!wasPaused)
-					_player->pause();
-				_vm->openMainMenuDialog();
-				if (!wasPaused)
-					_player->unpause();
+				openGameplayMainMenu();
 				return true;
 			}
 		}
@@ -966,12 +974,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				return true;
 			}
 
-			const bool wasPaused = _player->_paused;
-			if (!wasPaused)
-				_player->pause();
-			_vm->openMainMenuDialog();
-			if (!wasPaused)
-				_player->unpause();
+			openGameplayMainMenu();
 			return true;
 		}
 
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 2a90f61f59e..4327c5e1703 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -483,7 +483,16 @@ void InsaneRebel1::warpGameplayMouseNow(int x, int y) {
 		eventMan->purgeMouseEvents();
 }
 
+void InsaneRebel1::enableIOSGamepadController() {
+	_iosGamepadControllerState.enable();
+}
+
+void InsaneRebel1::restoreIOSGamepadController() {
+	_iosGamepadControllerState.restore();
+}
+
 InsaneRebel1::~InsaneRebel1() {
+	restoreIOSGamepadController();
 	_vm->_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
 	terminateAudio();
 	freeSfx();
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index d34ec43f0d1..b191854248b 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -27,6 +27,7 @@
 #include "common/events.h"
 #include "scumm/insane/insane.h"
 #include "scumm/insane/rebel/rebel_audio.h"
+#include "scumm/insane/rebel/rebel_gamepad.h"
 #include "scumm/smush/rebel/smush_player_ra1.h"
 
 namespace Scumm {
@@ -210,6 +211,9 @@ private:
 	void captureInteractiveVideoInput();
 	void releaseInteractiveVideoInput();
 	void playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame);
+	void enableIOSGamepadController();
+	void restoreIOSGamepadController();
+	void openGameplayMainMenu();
 	bool loadRA1Nut(const char *filename, RA1SpriteBank &bank, bool warnIfMissing = true);
 	void loadLevelSprites(int level);
 	void handleGameOpcode5EReset(uint32 param1);
@@ -521,6 +525,7 @@ private:
 	bool _interactiveVideoActive;
 	bool _preserveInteractiveRuntimeState;
 	bool _interactiveVideoCheatSkipped;
+	RebelIOSGamepadControllerState _iosGamepadControllerState;
 
 	// Path branching for levels with left/right alternative videos.
 	// Original sets nextSceneA/nextSceneB when GAME 0x07 counter == 394 (0x18A).
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 5cdd1704d49..92962e9b931 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1774,6 +1774,8 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
 	const bool preserveInputState = _preserveInteractiveRuntimeState || level7RouteSplice;
 
+	enableIOSGamepadController();
+
 	// Center mouse, hide system cursor (we draw our own), lock mouse to window.
 	// Some replays happen inside one original gameplay loop, so keep the current
 	// input state instead of recentering between route clips.
@@ -1802,6 +1804,7 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 void InsaneRebel1::releaseInteractiveVideoInput() {
 	_gameplayMouseSettleUntil = 0;
 	g_system->lockMouse(false);
+	restoreIOSGamepadController();
 }
 
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index f7fd926727e..a5d34e8b9fa 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -525,6 +525,7 @@ int InsaneRebel2::runLevel(int levelId) {
 	}
 
 	// Unlock the mouse when returning to menu
+	restoreIOSGamepadController();
 	g_system->lockMouse(false);
 	CursorMan.showMouse(true);
 
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 213e0820411..7b4781a1ffa 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -568,6 +568,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 
 
 InsaneRebel2::~InsaneRebel2() {
+	restoreIOSGamepadController();
 	setVirtualKeyboardVisible(false);
 
 	// Unregister EventObserver
@@ -625,7 +626,11 @@ void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
 	if (!splayer->_paused)
 		splayer->pause();
 
+	const bool restoreGamepad = _iosGamepadControllerState.isEnabled();
+	restoreIOSGamepadController();
 	_vm->openMainMenuDialog();
+	if (restoreGamepad && _gameState == kStateGameplay && _rebelHandler != 0 && !_vm->shouldQuit())
+		enableIOSGamepadController();
 	splayer->unpause();
 	_lastGameplayMenuCloseTime = _vm->_system->getMillis();
 }
@@ -641,6 +646,14 @@ void InsaneRebel2::openMenuMainMenu(SmushPlayer *splayer) {
 	_vm->openMainMenuDialog();
 }
 
+void InsaneRebel2::enableIOSGamepadController() {
+	_iosGamepadControllerState.enable();
+}
+
+void InsaneRebel2::restoreIOSGamepadController() {
+	_iosGamepadControllerState.restore();
+}
+
 // notifyEvent -- EventObserver callback for global input dispatch.
 // Handles ESC (skip) and SPACE (pause) regardless of menu state.
 // Pause behavior matches original FUN_405A21: SPACE pauses, ANY key unpauses.
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index b0bf580ba9b..099add0d6cd 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -28,6 +28,7 @@
 
 #include "scumm/insane/insane.h"
 #include "scumm/insane/rebel/rebel_audio.h"
+#include "scumm/insane/rebel/rebel_gamepad.h"
 
 #include "common/keyboard.h"
 #include "common/list.h"
@@ -424,6 +425,8 @@ public:
 	// flags and when to call processWaveEnd(). recordFrame preserves the original
 	// split between gameplay/wave calls and transition/init-only segments.
 	bool playLevelSegment(const char *filename, uint16 flags, bool recordFrame = true);
+	void enableIOSGamepadController();
+	void restoreIOSGamepadController();
 
 	int calculateAccuracy(int kills, int misses) const;
 
@@ -470,6 +473,7 @@ public:
 	void centerGameplayAim();
 	// Tracks consecutive recorded gameplay SANs so wave-loop videos do not recenter aim.
 	bool _gameplaySectionActive;
+	RebelIOSGamepadControllerState _iosGamepadControllerState;
 
 	// Level state tracking for multi-phase levels
 	int _currentPhase;        // Current gameplay phase (1, 2, 3 for Level 2; 1, 2 for Level 3/6)
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 1dd66088685..55a0e046ee1 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -197,12 +197,16 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 		if (!_gameplaySectionActive && (flags & 0x40) == 0)
 			centerGameplayAim();
 		_gameplaySectionActive = true;
+		enableIOSGamepadController();
 	} else {
 		_gameplaySectionActive = false;
+		restoreIOSGamepadController();
 	}
 
 	splayer->setCurVideoFlags(flags);
 	splayer->play(filename, 15);
+	if (isRecordedGameplay)
+		restoreIOSGamepadController();
 	if (recordFrame)
 		_deathFrame = splayer->_frame;
 	restoreDamageFlashPalette();
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 4f42d417fdc..c85694c0f0b 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -134,6 +134,7 @@ MODULE_OBJS += \
 	insane/insane_scenes.o \
 	insane/insane_iact.o \
 	insane/rebel/rebel_audio.o \
+	insane/rebel/rebel_gamepad.o \
 	insane/rebel1/rebel.o \
 	insane/rebel1/audio.o \
 	insane/rebel1/iact.o \




More information about the Scummvm-git-logs mailing list